调用和应用有什么区别?

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

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

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

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

790905 次浏览

斯科特·艾伦在这个问题上排名第0。

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

应用()方法与调用()相同,除了应用()需要一个数组作为第二个参数。数组代表目标方法的参数。”

所以:

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

不同之处在于apply允许您以arguments作为数组调用函数;call要求显式列出参数。一个有用的助记符是"A表示array,C表示comma。"

请参阅MDN在适用打电话上的留档。

伪语法:

theFunction.apply(valueForThis, arrayOfArgs)

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

从ES6开始,也有可能将数组#0call函数一起使用,您可以看到兼容性这里

示例代码:

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

要回答关于何时使用每个函数的部分,如果您不知道将要传递的参数数量,或者如果它们已经在数组或类似数组的对象中(例如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会更快。

虽然这是一个老生常谈的话题,但我只想指出. call比. apple稍微快一点。我不能确切地告诉你为什么。

请参阅jsPerf,http://jsperf.com/test-call-vs-apply/3


[UPDATE!]

道格拉斯·克罗克福德简要地提到了两者之间的区别,这可能有助于解释性能差异。

应用需要一个参数数组,而调用需要零个或多个单独的参数!啊哈!

.apply(this, [...])

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

我想展示一个例子,其中使用了'value eForThis'参数:

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*

这里有一个很好的助记符。一个p治使用一个rray,一个lways需要一个或两个参数。当你使用Call时,你必须Count参数的数量。

我们可以区分调用和应用方法如下

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

应用:调用以数组形式提供参数的函数。如果您不知道要传递给函数多少参数,您可以使用应用。

使用应用调用有一个优点,我们不需要改变参数的数量,只有我们可以改变传递的数组。

性能没有太大差异。但我们可以说调用比应用快一点,因为数组需要在应用方法中评估。

遵循关闭:迈克尔·博林的权威指南的摘录。它可能看起来有点长,但它充满了很多洞察力。来自“附录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浏览器中执行时,windows是全局对象。这导致了一些令人惊讶的行为:

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 NaNobj.addValues.call(undefined, 10, 20, 30);obj.addValues.apply(undefined, [10, 20, 30]);

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

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


提取结束。

这是一篇小文章,我在上面写道:

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 callexecute.call(obj1, "dan", "stanhope");//output: obj1 dan stanhope
//using applyexecute.apply(obj2, ["dan", "stanhope"]);//output: obj2 dan stanhope
//using old schoolexecute("dan", "stanhope");//output: undefined "dan" "stanhope"

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

.call(scope, arg1, arg2, arg3)

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

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

以下是一些用法示例:http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/

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

一个小代码示例:

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

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

基本的区别是call()接受参数列表,而apply()接受单个参数数组

尽管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;}
//Usagevar Car = makeClass({constructor: function(name){this.name=name;},yourName: function() {return this.name;}});//We have a Car class nowvar 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 Carvar superCar=new SuperCar('BMW xy',2.6);superCar.yourName();//Returns BMW xysuperCar.yourPower();// Returns 2.6

另一个例子是调用、应用和绑定。调用和应用之间的区别是显而易见的,但绑定是这样工作的:

  1. Bind返回可以执行的函数的实例
  2. 第一个参数是“这个
  3. 第二个参数是参数的逗号分隔列表(如打电话

}

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' prototypevar 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 invokedvar baseName = Person.prototype.getName.bind(this, "is a", "boy");console.log("Bind: " + baseName());}
reader.getName();/* OutputApply: John Smith is a boyCall: John Smith is a boyBind: John Smith is a boy*/

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

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

调用和应用都用于在执行函数时强制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很难实现。

Function.prototype.apply()上的MDN文档

应用()方法调用具有给定this值的函数作为数组(或类似数组的对象)提供的参数。

语法

fun.apply(thisArg, [argsArray])

Function.prototype.call()上的MDN文档

call()方法调用具有给定this值和单独提供的参数的函数。

语法

fun.call(thisArg[, arg1[, arg2[, ...]]])

JavaScript中的Function.apply和Function.call

应用()方法与调用()相同,除了应用()需要一个数组作为第二个参数。数组表示目标方法。


代码示例:

var doSomething = function() {var arr = [];for(i in arguments) {if(typeof this[arguments[i]] !== 'undefined') {arr.push(this[arguments[i]]);}}return arr;}
var output = function(position, obj) {document.body.innerHTML += '<h3>output ' + position + '</h3>' + JSON.stringify(obj) + '\n<br>\n<br><hr>';}
output(1, doSomething('one','two','two','one'));
output(2, doSomething.apply({one : 'Steven', two : 'Jane'}, ['one','two','two','one']));
output(3, doSomething.call({one : 'Steven', two : 'Jane'},'one','two','two','one'));

另见这个小提琴

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

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

虽然这个函数的语法几乎与应用(),根本的区别在于call()接受一个参数list,而应用()接受单个参数数组。

所以如你所见,没有太大的区别,但是仍然有一些情况我们更喜欢使用call()或应用()。例如,看下面的代码,它使用应用方法从MDN中查找数组中的最小和最大数字:

// min/max number in an arrayvar numbers = [5, 6, 2, 3, 7];
// using Math.min/Math.max applyvar 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]);

总结:

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

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

  • 使用call(),您必须正常输入参数(从第二个参数开始)
  • 对于apply(),您必须传入参数数组。

示例:

let obj = {val1: 5,val2: 10}
const summation = function (val3, val4) {return  this.val1 + this.val2 + val3 + val4;}
console.log(summation.apply(obj, [2 ,3]));// first we assign we value of this in the first arg// with apply we have to pass in an array

console.log(summation.call(obj, 2, 3));// with call we can pass in each arg individually

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

this值在javascript中有时会很棘手。this的值决定了当一个函数被执行时,而不是当一个函数被定义时。如果我们的函数依赖于右this绑定,我们可以使用call()apply()来强制执行此行为。例如:

var name = 'unwantedGlobalName';
const obj =  {name: 'Willem',sayName () { console.log(this.name);}}

let copiedMethod = obj.sayName;// we store the function in the copiedmethod variable


copiedMethod();// this is now window, unwantedGlobalName gets logged
copiedMethod.call(obj);// we enforce this to be obj, Willem gets logged

让我补充一点细节。

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

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

只有一个小区别:

  • spread操作符…允许传递可迭代args作为要调用的列表。
  • apply只接受类数组参数。

所以,这些调用是相辅相成的。我们期望迭代call起作用,我们期望类数组apply起作用。

对于同时是迭代类数组的对象,就像一个真正的数组,从技术上讲,我们可以使用它们中的任何一个,但应用可能是更快,因为大多数JavaScript引擎在内部优化得更好。

我只是想添加一个简单的例子来解释帖子 by平线,这对于初学者来说很容易理解。

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

我们还使用“Call”和“Ap唱”方法来更改参考,如下面的代码所定义

let Emp1 = {name: 'X',getEmpDetail: function(age, department) {console.log(`Name: ${this.name}    Age: ${age}    Department: ${department}`)}}
Emp1.getEmpDetail(23, 'Delivery')
// 1st approach of changing "this"let Emp2 = {name: 'Y',getEmpDetail: Emp1.getEmpDetail}
Emp2.getEmpDetail(55, 'Finance')
// 2nd approach of changing "this" using "Call" and "Apply"let Emp3 = {name: 'Emp3_Object',}
Emp1.getEmpDetail.call(Emp3, 30, 'Admin')
// here we have change the ref from **Emp1 to Emp3**  object// now this will print "Name =  Emp3_Object" because it is pointing to Emp3 objectEmp1.getEmpDetail.apply(Emp3, [30, 'Admin'])

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} just bought a ${this.name} from ${this.country}`);}}
const firstName = "Bryan";const lastName = "Smith";
car.showBuyer(firstName, lastName);  // Bryan just bought a Reno from France
const obj = { name: "Maserati", country: "Italy" };
car.showBuyer.call(obj, firstName, lastName); // Bryan Smith just bought a Maserati from Italy
car.showBuyer.apply(obj, [firstName, lastName]); // Bryan Smith just bought a Maserati from Italy