题 电话和申请有什么区别?


使用之间有什么区别 call 和 apply 调用一个函数?

var func = function() {
  alert('hello!');
};

func.apply(); VS func.call();

上述两种方法之间是否存在性能差异?何时最好使用 call 过度 apply 反之亦然?


2741
2017-12-31 19:56


起源


考虑到 a 在申请args和 c在调用args列。 - Larry Battle
@LarryBattle完全帮助我记住70-480认证的差异:) - Shouvik
@LarryBattle我做的几乎一样,但我认为是在申请数组和c来调用逗号(即逗号分隔的参数)。 - Samih
@LarryBattle很好的助记符拉里,终于记起了与名字的相关性 - Tomo
您 应用 一次工作(一个论点),你[电话] 呼叫 人多次(几个论点)。替代方案:有太多[太多]了 呼叫 职责游戏。 - Gras Double


答案:


不同之处在于 apply 让你调用函数 arguments 作为一个数组; call 要求显式列出参数。一个有用的助记符是 一个 对于 一个rray和 C 对于 COMMA“。

请参阅MDN的文档 应用 和 呼叫

伪语法:

theFunction.apply(valueForThis, arrayOfArgs)

theFunction.call(valueForThis, arg1, arg2, ...)

从ES6开始,还有可能 spread 用于的数组 call 功能,你可以看到兼容性 这里

示例代码:

function theFunction(name, profession) {
    console.log("My name is " + name + " and I am a " + profession +".");
}
theFunction("John", "fireman");
theFunction.apply(undefined, ["Susan", "school teacher"]);
theFunction.call(undefined, "Claude", "mathematician");
theFunction.call(undefined, ...["Matthew", "physicist"]); // used with the spread operator


3308
2017-12-31 20:00



要添加的一件事是args必须是数值数组([])。关联数组({})将不起作用。 - Kevin Schroeder
@KevinSchroeder:用javascript的说法, [] 被称为 排列, {} 被称为 目的。 - Martijn
我经常忘记哪个采用数组,并希望你列出参数。我曾经记得的一种技术是,如果方法的第一个字母开头 一个 然后需要一个数组,即 一个 pply数组 - aziz punjani
@SAM使用 呼叫 而不是正常的函数调用只有在需要更改值时才有意义 这个 用于函数调用。示例(将函数arguments-object转换为数组): Array.prototype.slice.call(arguments) 要么 [].slice.call(arguments)。 应用 如果你在数组中有参数,例如在一个用(几乎)相同的参数调用另一个函数的函数中,这是有意义的。 建议 使用普通函数调用 funcname(arg1) 如果这样做你需要的,并保存 呼叫 和 应用 对于那些你真正需要它们的特殊场合。 - some
@KunalSingh两个 call 和 apply 有两个参数。第一个论点 apply' and call`函数必须是owner对象,第二个参数分别是array或逗号分隔的参数。如果你通过 null 要么 undefined 作为第一个参数,然后在非严格模式中,它们被全局对象替换,即 window - A J Qarshi


K.斯科特艾伦有 一篇很好的文章 就此事。

基本上,他们在处理函数参数方面有所不同。

apply()方法与call()相同,但apply()需要数组作为第二个参数。该数组表示目标方法的参数。“

所以:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);

209
2017-12-31 19:59



apply()和call()的第二个参数是可选的,不是必需的。 - angry kiwi
第一个参数也不需要。 - Ikrom


要回答关于何时使用每个功能的部分,请使用 apply 如果您不知道要传递的参数数量,或者它们是否已经在数组或类数组对象中(如 arguments 反对转发自己的论点。使用 call 否则,因为不需要将参数包装在数组中。

f.call(thisObject, a, b, c); // Fixed number of arguments

f.apply(thisObject, arguments); // Forward this function's arguments

var args = [];
while (...) {
    args.push(some_value());
}
f.apply(thisObject, args); // Unknown number of arguments

当我没有传递任何论据时(比如你的例子),我更喜欢 call 因为我 调用 功能。 apply 会暗示你是 应用 (不存在的)参数的函数。

除非您使用,否则不应存在任何性能差异 apply 并将参数包装在一个数组中(例如 f.apply(thisObject, [a, b, c]) 代替 f.call(thisObject, a, b, c))。我没有测试它,所以可能会有差异,但它将是非常浏览器特定的。这可能是 call 如果你还没有数组中的参数,那么会更快 apply 如果你这样做会更快。


150
2017-12-31 21:50





这是一个很好的助记符。 一个pply使用 一个rrays和 一个lways需要一两个参数。当你使用 C所有你需要的 C数量参数的数量。


102
2017-09-04 13:36



那里有用的助记符!我将改变'一个或两个参数'来说'最多两个参数',因为它不是第一个或第二个参数 apply 是必须的。我不确定为什么会打电话 apply 要么 call 没有参数。看起来有人试图在这里找出原因 stackoverflow.com/questions/15903782/... - dantheta
那么,何时使用Call()以及何时使用Apply().... - Krish


虽然这是一个古老的话题,但我只想指出.call比.apply略快。我无法确切地告诉你原因。

见jsPerf, http://jsperf.com/test-call-vs-apply/3


[UPDATE!]

Douglas Crockford简要提到了两者之间的区别,这可能有助于解释性能差异...... http://youtu.be/ya4UHuXNygM?t=15m52s

Apply接受一组参数,而Call接受零个或多个单独的参数!啊哈!

.apply(this, [...])

.call(this, param1, param2, param3, param4...)


91
2017-11-07 17:36



这取决于函数对参数/数组的作用,如果它不需要处理数组,是否需要更少的时间? - Relic
有趣的是,即使没有阵列,呼叫仍然要快得多。 jsperf.com/applyvscallvsfn2 - Josh Mc
@JoshMc这将是特定于浏览器的。在IE 11中,我的申请速度是呼叫的两倍。 - Vincent McNabb
1.创建一个新数组意味着垃圾收集器需要在某个时候清理它。 2.使用取消引用访问数组中的项目比直接访问变量(参数)效率低。 (我相信这就是kmatheny所说的“解析”,这实际上是完全不同的东西。)但是我的论点都没有解释jsperf。这必须与引擎对两个功能的实现有关,例如也许他们无论如何都会创建一个空数组,如果没有传递的话。 - joeytwiddle
感谢您分享测试和视频 - Gary


跟随摘录 关闭:Michael Bolin的权威指南。它可能看起来有点冗长,但它充满了很多洞察力。从“附录B.经常误解的JavaScript概念”:


什么 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;
};
// Evaluates to 30 because obj is used as the value for 'this' when
// obj.addValues() is called, so obj.value becomes 10 + 20.
obj.addValues(20);

如果在调用函数时没有显式接收器,则全局对象成为接收器。如第47页的“goog.global”中所述,窗口是在Web浏览器中执行JavaScript时的全局对象。这会导致一些令人惊讶的行为:

var f = obj.addValues;
// Evaluates to NaN because window is used as the value for 'this' when
// f() is called. Because and window.value is undefined, adding a number to
// it results in NaN.
f(20);
// This also has the unintentional side effect of adding a value to window:
alert(window.value); // Alerts NaN

即使 obj.addValues 和 f 参考相同的函数,它们在被调用时表现不同,因为每次调用时接收器的值都不同。因此,在调用引用的函数时 this,确保这一点很重要 this 调用时将具有正确的值。要清楚,如果 this 没有在函数体中引用,然后是行为 f(20) 和 obj.addValues(20) 会是一样的。

因为函数是JavaScript中的第一类对象,所以它们可以有自己的方法。所有功能都有方法 call() 和 apply() 这使得重新定义接收器(即,对象)成为可能 this 在调用函数时指的是)。方法签名如下:

/**
* @param {*=} receiver to substitute for 'this'
* @param {...} parameters to use as arguments to the function
*/
Function.prototype.call;
/**
* @param {*=} receiver to substitute for 'this'
* @param {Array} parameters to use as arguments to the function
*/
Function.prototype.apply;

注意,唯一的区别 call() 和 apply() 就是它 call() 接收函数参数作为单独的参数,而 apply() 将它们作为单个数组接收:

// When f is called with obj as its receiver, it behaves the same as calling
// obj.addValues(). Both of the following increase obj.value by 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() 如果未指定,则使用其自己的接收器的值来替换receiver参数,以下内容将不起作用:

// Both statements evaluate to NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);

的价值 this 永远不可能 null 要么 undefined 当一个函数被调用。什么时候 null 要么 undefined 作为接收器提供 call() 要么 apply(),全局对象用作接收器的值。因此,前面的代码具有添加名为的属性的相同的不良副作用 value 到全球对象。

将函数视为不知道分配给它的变量可能会有所帮助。这有助于强化这样一种观点,即在调用函数时,而不是在定义函数时,它的值将被约束。


提取物结束。


71
2017-12-04 12:41



只是要注意这个事实 additionalValues 内部未引用 obj.addValues 身体 - Viktor Stolbin
我知道你正在回答这个问题,但是想补充一点:在定义f时你可以使用bind。 var f = obj.addValues; 变 var f = obj.addValues.bind(obj)   现在f(20)无需使用电话或每次申请都可以使用。 - jhliberty


有时一个对象借用另一个对象的函数是有用的,这意味着借用对象只是执行lent函数,就像它自己一样。

一个小代码示例:

var friend = {
    car: false,
    lendCar: function ( canLend ){
      this.car = canLend;
 }

}; 

var me = {
    car: false,
    gotCar: function(){
      return this.car === true;
  }
};

console.log(me.gotCar()); // false

friend.lendCar.call(me, true); 

console.log(me.gotCar()); // true

friend.lendCar.apply(me, [false]);

console.log(me.gotCar()); // false

这些方法对于为对象提供临时功能非常有用。


33
2018-02-25 19:31



对于想要知道如何看待的人 console.log 查看: 什么是console.log以及如何使用它? - Michel Ayres


Call,Apply和Bind的另一个例子。 Call和Apply之间的区别很明显,但是 捆绑 像这样工作:

  1. 绑定返回可以执行的函数的实例
  2. 第一个参数是'这个
  3. 第二个参数是a 逗号分开了 参数列表(如 呼叫

}

function Person(name) {
    this.name = name; 
}
Person.prototype.getName = function(a,b) { 
     return this.name + " " + a + " " + b; 
}

var reader = new Person('John Smith');

reader.getName = function() {
   // Apply and Call executes the function and returns value

   // Also notice the different ways of extracting 'getName' prototype
   var baseName = Object.getPrototypeOf(this).getName.apply(this,["is a", "boy"]);
   console.log("Apply: " + baseName);

   var baseName = Object.getPrototypeOf(reader).getName.call(this, "is a", "boy"); 
   console.log("Call: " + baseName);

   // Bind returns function which can be invoked
   var baseName = Person.prototype.getName.bind(this, "is a", "boy"); 
   console.log("Bind: " + baseName());
}

reader.getName();
/* Output
Apply: John Smith is a boy
Call: John Smith is a boy
Bind: John Smith is a boy
*/

23
2018-03-31 07:32