ChatGPT解决这个技术问题 Extra ChatGPT

call 和 apply 和有什么不一样?

使用 Function.prototype.apply()Function.prototype.call() 调用函数有什么区别?

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

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

上述两种方法之间是否存在性能差异?什么时候最好使用 call 而不是 apply,反之亦然?

在申请参数数组时考虑 a,在调用参数列时考虑 c
@LarryBattle我几乎做同样的事情,但我认为 a in 申请数组和 c 调用逗号(即逗号分隔的参数)。
我同意这是愚蠢的。令人讨厌的是,在面试中不知何故被问到这个问题,因为一些有影响力的笨蛋把这个问题添加到了他们的重要 js 问题列表中。
你申请了一次工作(一次争论),你[电话]给别人打电话(多次争论)。替代方案:有[太多?]许多使命召唤游戏。
当意图使用参数值列表调用可变参数函数时,不管“this”值如何,请使用 ES6 扩展运算符,例如 fn(...input),其中输入是一个数组。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

Z
Zsolt Meszaros

不同之处在于 apply 允许您使用 arguments 作为数组调用函数; call 要求明确列出参数。一个有用的助记符是 A 表示 a 射线,C 表示 comma。”

请参阅 MDN 关于 applycall 的文档。

伪语法:

theFunction.apply(valueForThis, arrayOfArgs)

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

从 ES6 开始,还可以使用 spread 数组与 call 函数一起使用,您可以看到兼容性 here

示例代码:

function theFunction(name,professional) { console.log("我叫"+name+",我是"+professional+"。"); } theFunction("约翰", "消防员"); theFunction.apply(undefined, ["Susan", "学校老师"]); theFunction.call(undefined, "克劳德", "数学家"); theFunction.call(undefined, ...["Matthew", "physicist"]); // 与扩展运算符一起使用


要添加的一件事是 args 必须是数字数组 ([])。关联数组 ({}) 将不起作用。
@KevinSchroeder:在 javascript 中,[] 被称为 array{} 被称为 object
我经常忘记哪个需要一个数组,哪个需要你列出参数。我曾经记得的一个技巧是,如果方法的第一个字母以 a 开头,那么它需要一个数组,即一个应用数组
@SAM 使用 call 而不是普通的函数调用只有在您需要更改函数调用的 this 值时才有意义。一个示例(将函数参数对象转换为数组):Array.prototype.slice.call(arguments)[].slice.call(arguments)apply 如果您在数组中有参数,例如在调用具有(几乎)相同参数的另一个函数的函数中,则 apply 是有意义的。 建议 如果可以满足您的需要,请使用普通函数调用 funcname(arg1),并在您真正需要的特殊场合保存 callapply他们。
@KunalSingh callapply 都有两个参数。 apply' and call 函数的第一个参数必须是所有者对象,第二个参数将分别是数组或逗号分隔的参数。如果您将 nullundefined 作为第一个参数传递,那么在非严格模式下,它们将被替换为全局对象,即 window
n
neoswf

K. Scott Allen 对此有 a nice writeup

基本上,它们在处理函数参数的方式上有所不同。

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

所以:

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

apply() 和 call() 的第二个参数是可选的,不是必需的。
第一个参数也不是必需的。
@Ikrom,第一个参数不是 call 所必需的,而是 apply 所必需的
M
Matthew Crumley

要回答有关何时使用每个函数的部分,如果您不知道要传递的参数数量,或者如果它们已经在数组或类似数组的对象中(如 arguments 对象),请使用 apply转发您自己的参数。否则使用 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 可能会更快。


n
nhahtdh

这是一个很好的助记符。 Apply 使用数组并且总是使用一两个参数。当您使用 Call 时,您必须计算参数的数量。


有用的助记符就在那里!我会将“一个或两个参数”更改为“最多两个参数”,因为 apply 的第一个或第二个参数都不是必需的。我不确定为什么会在没有参数的情况下调用 applycall。似乎有人试图找出这里的原因stackoverflow.com/questions/15903782/…
k
kmatheny

虽然这是一个老话题,但我只想指出 .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...)


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

遵循 Closure: The Definitive Guide by 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”中所述,当 JavaScript 在 Web 浏览器中执行时,window 是全局对象。这导致了一些令人惊讶的行为:

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.addValuesf 引用相同的函数,它们在调用时的行为也会有所不同,因为接收者的值在每次调用中都不同。因此,在调用引用 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]);

以下调用是等效的,因为 fobj.addValues 指的是同一个函数:

obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);

但是,由于 call()apply() 都没有使用自己的接收者的值来代替未指定的接收者参数,因此以下内容将不起作用:

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

调用函数时,this 的值永远不能是 nullundefined。当 nullundefined 作为接收器提供给 call()apply() 时,全局对象将用作接收器的值。因此,前面的代码同样具有向全局对象添加一个名为 value 的属性的不良副作用。

将函数视为不知道分配给它的变量可能会有所帮助。这有助于强化这样的想法,即 this 的值将在调用函数时而不是在定义函数时绑定。

提取结束。


请注意,additionalValues 未在 obj.addValues 正文中引用
我知道你在回答这个问题,但想补充一点:你可以在定义 f 时使用 bind。 var f = obj.addValues; 变为 var f = obj.addValues.bind(obj),现在 f(20) 无需每次都使用 call 或 apply 即可工作。
我知道这不是你写的,但你确实强调了书中相关的文字和示例,我非常感激。他们非常有帮助。
t
tjacks3

有时,一个对象借用另一个对象的功能很有用,这意味着借用的对象只是简单地执行借出的功能,就好像它是自己的一样。

一个小代码示例:

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

这些方法对于赋予对象临时功能非常有用。


想知道如何查看console.log的人请查看:What is console.log and how do I use it?
M
Mahesh

调用、应用和绑定的另一个例子。 Call 和 Apply 之间的区别很明显,但 Bind 的工作方式如下:

Bind 返回一个可以执行的函数实例 第一个参数是 'this' 第二个参数是逗号分隔的参数列表(如调用)

}

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
*/

佚名

我想展示一个示例,其中使用了“valueForThis”参数:

Array.prototype.push = function(element) {
   /*
   Native code*, that uses 'this'       
   this.put(element);
   */
}
var array = [];
array.push(1);
array.push.apply(array,[2,3]);
Array.prototype.push.apply(array,[4,5]);
array.push.call(array,6,7);
Array.prototype.push.call(array,8,9);
//[1, 2, 3, 4, 5, 6, 7, 8, 9] 

**详情:http://es5.github.io/#x15.4.4.7*


D
Dean Radcliffe

Call() 接受逗号分隔的参数,例如:

.call(scope, arg1, arg2, arg3)

并且 apply() 接受一个参数数组,例如:

.apply(scope, [arg1, arg2, arg3])

这里还有几个使用示例:http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/


` // call() === 逗号分隔的参数 (arguments-list) .call(this, args1, args2, args3, ...) // apply() === 参数数组 (array-items) .应用(这个,[arr0,arr1,arr2,...])`
J
John Slegers

the MDN docs on Function.prototype.apply()

apply() 方法使用给定的 this 值和作为数组(或类似数组的对象)提供的参数调用函数。语法 fun.apply(thisArg, [argsArray])

the MDN docs on Function.prototype.call()

call() 方法使用给定的 this 值和单独提供的参数调用函数。语法 fun.call(thisArg[, arg1[, arg2[, ...]]])

Function.apply and Function.call in JavaScript

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

代码示例:

var doSomething = function() { var arr = []; for(i in arguments) { if(typeof this[arguments[i]] !== 'undefined') { arr.push(this[arguments[i]]); } } 返回 arr; } var output = function(position, obj) { document.body.innerHTML += '

output ' + position + '

' + JSON.stringify(obj) + '\n
\n< br><小时>'; } 输出(1,doSomething(“一”,“二”,“二”,“一”));输出(2,doSomething.apply({一:'史蒂文',二:'简'},['一','二','二','一']));输出(3,doSomething.call({一个:'史蒂文',两个:'简'},'一个','两个','两个','一个'));

另见this Fiddle


D
Dan

这是一个小帖子,我在上面写道:

http://sizeableidea.com/call-versus-apply-javascript/

var obj1 = { which : "obj1" },
obj2 = { which : "obj2" };

function execute(arg1, arg2){
    console.log(this.which, arg1, arg2);
}

//using call
execute.call(obj1, "dan", "stanhope");
//output: obj1 dan stanhope

//using apply
execute.apply(obj2, ["dan", "stanhope"]);
//output: obj2 dan stanhope

//using old school
execute("dan", "stanhope");
//output: undefined "dan" "stanhope"

这是另一个:blog.i-evaluation.com/2012/08/15/javascript-call-and-apply 但基本上是正确的:.call(scope, arg1, arg2, arg3)
R
Rakesh Kumar

根本区别在于 call() 接受 参数列表,而 apply() 接受 单个参数数组 .


H
Hkachhia

不同之处在于 call() 分别采用函数参数,而 apply() 采用数组中的函数参数。


W
Willem van der Veen

概括:

call()apply() 都是位于 Function.prototype 上的方法。因此它们可以通过原型链在每个函数对象上使用。 call()apply() 都可以执行具有指定 this 值的函数。

call()apply() 之间的主要区别在于您必须将参数传递给它的方式。在 call()apply() 中,您将希望作为值的对象作为第一个参数传递为 this。其他参数的不同之处如下:

使用 call() 您必须正常输入参数(从第二个参数开始)

使用 apply() 您必须传入参数数组。

例子:

让 obj = { val1: 5, val2: 10 } const summation = function (val3, val4) { return this.val1 + this.val2 + val3 + val4; } console.log(summation.apply(obj, [2 ,3])); // 首先我们在第一个参数中分配 this 的值 // 使用 apply 我们必须传入一个数组 console.log(summation.call(obj, 2, 3)); // 通过调用,我们可以单独传入每个参数

为什么我需要使用这些功能?

this 值有时在 javascript 中可能很棘手。 this 的值决定了函数何时执行而不是函数定义。如果我们的函数依赖于正确的 this 绑定,我们可以使用 call()apply() 来强制执行这种行为。例如:

var name = 'unwantedGlobalName'; const obj = { name: 'Willem', sayName () { console.log(this.name);} } let mappedMethod = obj.sayName; // 我们将函数存储在复制的方法变量中copyedMethod(); // 现在是窗口,不需要的GlobalName 被记录// 我们强制它为 obj,Willem 被记录


P
Praveen D

我们可以区分 call 和 apply 方法如下

CALL :带参数的函数单独提供。如果您知道要传递的参数或没有要传递的参数,则可以使用 call。

APPLY :使用作为数组提供的参数调用函数。如果您不知道要传递给函数的参数数量,则可以使用 apply。

使用 apply 而不是调用有一个优点,我们不需要更改参数的数量,只需更改传递的数组即可。

性能上差别不大。但是我们可以说 call 比 apply 快一点,因为数组需要在 apply 方法中进行评估。


A
Alireza

主要区别在于,使用 call,我们可以更改范围并照常传递参数,但 apply 允许您使用参数作为数组调用它(将它们作为数组传递)。但就它们在您的代码中执行的操作而言,它们非常相似。

虽然此函数的语法与 apply() 的语法几乎相同,但根本区别在于 call() 接受参数列表,而 apply() 接受单个参数数组。

如您所见,差别不大,但在某些情况下我们更喜欢使用 call() 或 apply()。例如,看下面的代码,它使用 apply 方法从 MDN 中找到数组中的最小和最大数:

// min/max number in an array
var numbers = [5, 6, 2, 3, 7];

// using Math.min/Math.max apply
var max = Math.max.apply(null, numbers); 
// This about equal to Math.max(numbers[0], ...)
// or Math.max(5, 6, ...)

var min = Math.min.apply(null, numbers)

所以主要区别只是我们传递参数的方式:

function.call(thisArg, arg1, arg2, ...);

申请:

function.apply(thisArg, [argsArray]);

v
venkat7668

这些方法之间的区别在于,您希望如何传递参数。

“A 表示数组,C 表示逗号”是一个方便的助记符。


这个答案提供了什么其他答案中尚未提供的内容?
R
Raghavendra

Call 和 apply 都用于在执行函数时强制 this 值。唯一的区别是 call 采用 n+1 个参数,其中 1 是 this'n' argumentsapply 只接受两个参数,一个是 this,另一个是参数数组。

我在 apply 中看到的优于 call 的优点是我们可以轻松地将函数调用委托给其他函数,而无需付出太多努力;

function sayHello() {
  console.log(this, arguments);
}

function hello() {
  sayHello.apply(this, arguments);
}

var obj = {name: 'my name'}
hello.call(obj, 'some', 'arguments');

观察我们使用 applyhello 委派给 sayHello 是多么容易,但使用 call 则很难实现。


D
Dhana Krishnasamy

尽管 callapply 实现了相同的目标,但我认为至少有一个地方您不能使用 call 而只能使用 apply。那是当你想要支持继承并想要调用构造函数的时候。

这是一个允许您创建类的函数,该函数还支持通过扩展其他类来创建类。

function makeClass( properties ) {
    var ctor = properties['constructor'] || function(){}
    var Super = properties['extends'];
    var Class = function () {
                 // Here 'call' cannot work, only 'apply' can!!!
                 if(Super)
                    Super.apply(this,arguments);  
                 ctor.apply(this,arguments);
                }
     if(Super){
        Class.prototype = Object.create( Super.prototype );
        Class.prototype.constructor = Class;
     }
     Object.keys(properties).forEach( function(prop) {
           if(prop!=='constructor' && prop!=='extends')
            Class.prototype[prop] = properties[prop];
     });
   return Class; 
}

//Usage
var Car = makeClass({
             constructor: function(name){
                         this.name=name;
                        },
             yourName: function() {
                     return this.name;
                   }
          });
//We have a Car class now
 var carInstance=new Car('Fiat');
carInstance.youName();// ReturnsFiat

var SuperCar = makeClass({
               constructor: function(ignore,power){
                     this.power=power;
                  },
               extends:Car,
               yourPower: function() {
                    return this.power;
                  }
              });
//We have a SuperCar class now, which is subclass of Car
var superCar=new SuperCar('BMW xy',2.6);
superCar.yourName();//Returns BMW xy
superCar.yourPower();// Returns 2.6

我相信 call 可以与所选答案中所述的价差运算符一起使用。除非我错过了什么。
P
Pravin Divraniya

让我对此添加一些细节。

这两个调用几乎是等价的:

func.call(context, ...args); // pass an array as list with spread operator

func.apply(context, args);   // is same as using apply

只有一点点区别:

扩展运算符 ... 允许将可迭代参数作为要调用的列表传递。 apply 只接受类似数组的参数。

因此,这些调用相互补充。在我们期望 iterable 的地方,call 有效,在我们期望 类数组 的地方,apply 有效。

对于既可迭代又类似数组的对象,比如真正的数组,从技术上讲,我们可以使用它们中的任何一个,但 apply 可能会更快,因为大多数 JavaScript 引擎在内部对其进行了更好的优化。


R
Ran Turner

call() 方法调用具有给定 this 值和第二个参数的函数,这些参数是以逗号分隔的参数

object.someMethod.call( someObject, arguments )

apply() 方法与 call 相同,只是它采用的第二个参数是一个参数数组

object.someMethod.apply( someObject, arrayOfarguments )

var car = { name: "Reno", country: "France", showBuyer: function(firstName, lastName) { console.log(`${firstName} ${lastName} 刚从 ${ 买了 ${this.name} this.country}`); } } const firstName = "布莱恩"; const lastName = "史密斯"; car.showBuyer(firstName, lastName); // Bryan 刚从法国买了一辆 Reno const obj = { name: "Maserati", country: "Italy" }; car.showBuyer.call(obj, firstName, lastName); // Bryan Smith 刚从意大利买了一辆玛莎拉蒂 car.showBuyer.apply(obj, [firstName, lastName]); // Bryan Smith 刚从意大利买了一辆玛莎拉蒂


A
Abdul Rehman Kaim Khani

我只是想在flatline 解释清楚的post 中添加一个简单的例子,让初学者容易理解。

func.call(context, args1, args2 );   // pass arguments as "," separated value

func.apply(context, [args1, args2]); // pass arguments as "Array"

我们还使用“调用”和“应用”方法来更改下面代码中定义的引用

让 Emp1 = { name: 'X', getEmpDetail: function(age, department) { console.log(`Name: ${this.name} Age: ${age} Department: ${department}`) } } Emp1. getEmpDetail(23, 'Delivery') // 改变“this”的第一种方法 let Emp2 = { name: 'Y', getEmpDetail: Emp1.getEmpDetail } Emp2.getEmpDetail(55, 'Finance') // 改变“this”的第二种方法this" using "Call" 和 "Apply" let Emp3 = { name: 'Emp3_Object', } Emp1.getEmpDetail.call(Emp3, 30, 'Admin') // 这里我们将 ref 从 **Emp1 更改为 Emp3* * object // 现在这将打印“Name = Emp3_Object”,因为它指向 Emp3 对象 Emp1.getEmpDetail.apply(Emp3, [30, 'Admin'])


i
itsanewabstract

https://i.stack.imgur.com/vwr0B.jpg

https://i.stack.imgur.com/orgBS.jpg


尽管我们感谢您为制作图像所做的努力,但它们并不是很有用。没有人可以复制代码来使用、测试、改进...代码应该在这里粘贴为文本