来来来,首先来个栗子解释一下:
要先明白存在 call 和 apply 的原因,才能记得牢一点,在 javascript OOP 中,我们经常会这样定义:
function cat(){}
cat.prototype={
food:"fish",
say: function(){
alert("I love "+this.food);
}
}
var blackCat = new cat;
blackCat.say();
但是如果我们有一个对象 whiteDog = {food:"bone"}
,我们不想对它重新定义 say 方法,那么我们可以通过 call 或 apply 用 blackCat 的 say 方法:blackCat.say.call(whiteDog);
所以,可以看出 call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法,但是其他的有,我们可以借助 call 或 apply 用其它对象的方法来操作。
用的比较多的,通过 document.getElementsByTagName
选择的 dom 节点是一种类似 array 的 array。它不能应用 Array 下的 push, pop 等方法。我们可以通过:
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
这样 domNodes
就可以应用 Array 下的所有方法了。
来来来 再来一点官方的解释:
1.每个函数都包含两个非继承而来的方法:apply() 和 call(),这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。
首先,apply() 方法接收两个参数:一个是在其中运行的函数作用域,另一个是参数数组。其中,第二个参数可以是 array 的实例,也可以是 arguments 对象。
function sum(num1,num2){
rerturn num1 + num2;
}
function callSum1(num1,num2){
return sum.apply(this,arguments); //传入 arguments 对象
}
function callSum2(num1,num2){
return sum.apply(this,[num1,num2]); // 传入数组
}
alert(callSum1(10,10)); //20
alert(callSum2(10,10)); //20
在上面的例子中,callSum1() 在执行 sum() 函数时传入了 this 作为 this 值(因为是在全局作用域中调用的,所以传入的就是 window 对象)和 arguments 对象。而 callSum2() 同样也调用了 sum() 函数,但它传入的则是 this 和一个参数数组。
2.call() 方法和 apply() 方法的作用相同,他们的区别仅仅在于接收参数的方式不同。对于 call() 方法而言,第一个参数是 this 值没有变化,变化的是其余的参数都直接传递给函数。话句话说,在使用 call() 方法时,传递给函数的参数必须逐个列出来,也就是说,你必须明确的传入每一个参数。
3.至于使用 apply() 还是 call(),完全取决于你采取哪一种给函数传递参数的方式最方便。如果你打算直接传入 arguments 对象,或者包含函数中先接收到的也是一个数组,那么使用 apply() 肯定更加方便;否则,使用 call() 可能更加合适。(在不给函数传递参数的情况下,使用那种 方法都无所谓)。
4.事实上,传递参数并非 apply() 和 call() 的真正用武之地,他们真正强大的地方在于能够扩充函数赖以运行的作用域。
window.color = 'red';
var o = {color:'blue'};
function sayColor(){
alert(this.color);
}
sayColor(); // red
sayColor(); // red
sayColor(window); // red
sayColor(o); // blue
sayColor() 是作为全局函数而定义的,而且当在全局作用域中调用它时,它确实会显示‘red’,因为对 this.color 的求值会转换成对 window.color 的求值。而 sayColor.call(this) 和 sayColor.call(window) 则是两种显示的在全局作用域中调用函数的方式,结果也都是 red。但是当运行 sayColor.call(o) 时,函数的执行环境就不一样了,因为此时函数体内的 this 对象指向了 o,于是结果显示的是‘blue’。
使用 call() 或 apply() 最大的好处就是对象不需要与方法有任何的耦合关系。