- Published on
JavaScript中的call与apply的区别
- Authors
- Name
在JavaScript编程中,理解call
和apply
函数方法及其区别非常重要。这两者在函数调用中提供了灵活的this
值绑定机制。接下来,我们通过具体代码示例详细解析这两个方法的用途和不同之处。
this
指代什么
当函数被调用时当调用形如foo.bar.baz()
形式的函数时,foo.bar
对象被称为接收者。在函数被调用时,使用接收者作为this
值:
var obj = {};
obj.value = 10;
/** @param {...number} additionalValues */
obj.addValues = function(additionalValues) {
for (var i = 0; i < arguments.length; i++) {
this.value += arguments[i];
}
return this.value;
};
// 因为调用obj.addValues()时使用obj作为this的值,所以obj.value变为10 + 20,结果为30。
obj.addValues(20);
如果调用函数时没有明确的接收者,那么全局对象将成为接收者。在网页浏览器中,全局对象是window
,这会带来一些意想不到的行为:
var f = obj.addValues;
// 由于f()调用时使用window作为this的值,而window.value未定义,所以结果为NaN。
f(20);
// 这也会在window中添加一个新的value属性:
alert(window.value); // 弹出NaN
即使obj.addValues
和f
引用的是同一个函数,由于接收者不同,它们的行为也会不同。因此,当调用一个引用this
的函数时,确保this
具有正确的值非常重要。需要明确的是,如果函数体中没有引用this
,那么f(20)
和obj.addValues(20)
的行为是一样的。
call
和apply
方法
由于JavaScript中的函数是第一类对象,它们可以有自己的方法。所有函数都有call()
和apply()
方法,这使得在调用函数时可以重新定义接收者。方法的签名如下:
/**
* @param {*=} receiver 用于替代'this'的接收者
* @param {...} parameters 用于函数调用的参数
*/
Function.prototype.call;
/**
* @param {*=} receiver 用于替代'this'的接收者
* @param {Array} parameters 用于函数调用的参数数组
*/
Function.prototype.apply;
需要注意的是,call()
和apply()
之间唯一的区别是call()
接收函数参数作为独立的参数,而apply()
接收它们作为一个数组:
// 当f被用obj作为接收者调用时,它的行为与调用obj.addValues()一样。以下两种调用都将obj.value增加60:
f.call(obj, 10, 20, 30);
f.apply(obj, [10, 20, 30]);
以下调用是等价的,因为f
和obj.addValues
引用的是同一个函数:
obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);
然而,由于无论call()
还是apply()
在未指定接收者参数时都不会使用它自身的接收者值,这样的调用是无效的:
// 两个语句都计算为NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);
当函数被调用时,this
的值永不能为null
或undefined
。当null
或undefined
作为接收者传递给call()
或apply()
时,全局对象会作为接收者的值。因此,上述代码带有将value
属性添加到全局对象的相同不良副作用。
将一个函数视为对它被分配到的变量一无所知可能会有所帮助。这有助于强化这样一个概念:this
的值在函数被调用时绑定,而不是在函数定义时绑定。
结论
理解JavaScript中的call
和apply
的使用及其区别,可以大大提高代码的灵活性和可维护性。在函数调用过程中,灵活地控制this
的值有助于避免许多意想不到的问题。