对javascript的call()和apply()这两个方法可以说是既熟悉又陌生,熟悉的是经常会在面试或者平时工作中遇到,陌生的是对它俩一直都是一知半解。刚好最近工作不是太忙,抽出时间来学习一下这两个方法。
1、定义
在javascript中,call和apply都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部this的指向。
function cat(){}
cat.prototype = {
food:'fish',
eat:function(){
console.log('i like to eat '+ this.food)
}
}
var whiteCat = new cat;
whiteCat.eat();
现在我们又有一个对象dog = {food:‘bone’},我不想对它重新定义eat方法,这时我们就可以通过call或apply来调用whiteCat的eat方法。
var dog = {food:'bone'}
whiteCat.eat.call(dog) //i like to eat bone
whiteCat.eat.apply(dog) //i like to eat bone
所以从上面的结果我们就可以总结出:当某一个对象没有某个方法(dog没有eat方法),但是其他对象有,此时我们就可以通过call或apply用其他对象的方法来操作。
2、用法
call和apply的调用基本一样,唯一不同的是传参。
function.call(thisArg, arg1, arg2, ...)
thisArg:可选的,在function函数运行时使用的this值。
arg1,arg2,…:指定的参数列表
func.apply(thisArg, [argsArray])
thisArg:必选的,在func函数运行时使用的this值。
argsArray:可选的,一个数组或类数组对象,其中的数组元素将作为单独的参数传给func函数。
apply()的其他用法
apply有一个巧妙的用处,就是可以将一个数组默认的转换为一个参数列表([param1,param2,param3]转换为param1,param2,param3),借助这个特性,可以在某些情况下提高开发效率:
1)Math.max可以实现得到数组中最大的一项
var arr = [1,2,3,4];
var max = Math.max.apply(this,arr)
console.log(max); // 4
因为Math.max参数里面不支持Math.max([param1,param2]),也就是不支持数组,但是它支持Math.max([param1,param2),所以可以根据apply的那个特点来解决。
2)Math.min可以实现得到数组中最小的一项
和Math.max一样
var arr = [1,2,3,4];
var min = Math.min.apply(this,arr)
console.log(min); // 1
3)Array.prototype.push可以实现两个数组合并
数组的push方法不能push一个数组,但是它可以push(param1,param2),所以我们可以通过apply来转换一下这个数组
var arr1 = [1,1,1]
var arr2 = [2,2,2]
Array.prototype.push.apply(arr1,arr2);
console.log(arr1); //[1, 1, 1, 2, 2, 2]
3、实现函数call和apply
随着前端技术的不断更新,市场对前端开发人员的要求也越来越高。在很多面试中,面试官不仅要求你会使用call和apply,还会要求你自己实现一个call或者apply函数。在前面我们讲了call和apply的应用场景:就是一个对象没有某个方法,我们就借用有这个方法的其他对象来调用此方法达到我们的目的。跟据这样的需求,我们实现函数的步骤就出来了:
- 将我们需要用到的函数设为对象的属性
- 执行该函数
- 删除该函数
Function.prototype.selfCall = function (context) {
//参数可以传null,当为null的时候,视为指向window
var context = context || window;
context.fn = this;
var args = [];
//call可以传参,并且参数不确定 我们就从Arguments对象中取值,
//注意是从第二个开始取值的
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
//在这里我们使用了eval函数,eval()可计算某个字符串,并执行其中的javascript代码
//当然可以用es6的方法,之所以用eval()是因为call就是es3的方法
var result = eval('context.fn(' + args +')');
//删除添加的函数
delete context.fn
return result;
}
代码不是太难,上面都有注释,如果哪一步不太懂,自己打断点走一遍应该就会明白了,也可以点击这里,一位大佬讲的,非常详细。
apply的实现和call类似,就直接贴代码了哈
Function.prototype.selfApply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
} else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}