logo
Published on

JavaScript中的call与apply的区别

Authors
  • Name
    Twitter

在JavaScript编程中,理解callapply函数方法及其区别非常重要。这两者在函数调用中提供了灵活的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.addValuesf引用的是同一个函数,由于接收者不同,它们的行为也会不同。因此,当调用一个引用this的函数时,确保this具有正确的值非常重要。需要明确的是,如果函数体中没有引用this,那么f(20)obj.addValues(20)的行为是一样的。

callapply方法

由于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]);

以下调用是等价的,因为fobj.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的值永不能为nullundefined。当nullundefined作为接收者传递给call()apply()时,全局对象会作为接收者的值。因此,上述代码带有将value属性添加到全局对象的相同不良副作用。

将一个函数视为对它被分配到的变量一无所知可能会有所帮助。这有助于强化这样一个概念:this的值在函数被调用时绑定,而不是在函数定义时绑定。

结论

理解JavaScript中的callapply的使用及其区别,可以大大提高代码的灵活性和可维护性。在函数调用过程中,灵活地控制this的值有助于避免许多意想不到的问题。