What is the difference between call and apply?
使用
1 2 3 | var func = function() { alert('hello!'); }; |
上述两种方法之间是否存在性能差异?何时最好使用
不同的是,
请参阅MDN关于Apply和Call的文档。
伪语法:
从ES6开始,还有
样例代码:
1 2 3 4 5 6 7 | 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 |
斯科特·艾伦对这件事有一个很好的描述。
基本上,它们在处理函数参数的方式上是不同的。
The apply() method is identical to call(), except apply() requires an array as the second parameter. The array represents the arguments for the target method."
所以:
1 2 3 4 | // assuming you have f function f(message) { ... } f.call(receiver,"test"); f.apply(receiver, ["test"]); |
要回答有关何时使用每个函数的部分,如果您不知道将要传递的参数数量,或者如果它们已经在数组或类似数组的对象(如
1 2 3 4 5 6 7 8 9 | 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 |
当我不传递任何参数(如您的示例)时,我更喜欢
不应该有任何性能差异,除非您使用
这是一个很好的记忆法。Apply使用数组,并且始终接受一个或两个参数。使用call时,必须计算参数的数目。
虽然这是一个古老的话题,但我只是想指出,呼叫比.apply快一点。我不能确切地告诉你原因。
参见jspef,http://jspef.com/test-call-vs-apply/3
[
道格拉斯·克罗克福德简单地提到了两者之间的区别,这可能有助于解释性能差异…http://youtu.be/ya4uhuxnygm?t=15M52S
Apply接受一个参数数组,而Call接受零个或多个单独的参数!啊哈!
以下是结尾部分的摘录:迈克尔·博林的权威指南。它可能看起来有点冗长,但它充满了许多洞察力。来自"附录B.经常误解javascript概念":
调用函数时,当调用
1 2 3 4 5 6 7 8 9 10 11 12 | 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时,window是全局对象。这会导致一些令人惊讶的行为:
1 2 3 4 5 6 7 | 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 |
尽管
因为函数是JavaScript中的第一类对象,所以它们可以有自己的方法。所有函数都有方法
1 2 3 4 5 6 7 8 9 10 | /** * @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; |
注意,
1 2 3 4 | // 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]); |
以下调用是等效的,因为
1 2 | obj.addValues.call(obj, 10, 20, 30); obj.addValues.apply(obj, [10, 20, 30]); |
但是,由于
1 2 3 | // Both statements evaluate to NaN obj.addValues.call(undefined, 10, 20, 30); obj.addValues.apply(undefined, [10, 20, 30]); |
调用函数时,
把一个函数看作不知道它被分配到的变量,这可能是有帮助的。这有助于强化这样一种观点,即当调用函数而不是定义函数时,将绑定该函数的值。
提取结束。
有时一个对象借用另一个对象的函数是有用的,这意味着借用对象只是简单地执行lent函数,就好像它是自己的一样。
一个小代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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 |
这些方法对于赋予对象临时功能非常有用。
调用、应用和绑定的另一个示例。调用和应用之间的区别很明显,但bind的工作方式如下:
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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"参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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*
call()采用逗号分隔的参数,例如:
apply()接受一个参数数组,例如:
下面是一些更多的用法示例:http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/
来自函数.prototype.apply()上的MDN文档:
The apply() method calls a function with a given
this value and
arguments provided as an array (or an array-like object).Syntax
1 fun.apply(thisArg, [argsArray])
来自函数.prototype.call()上的MDN文档:
The call() method calls a function with a given
this value and arguments provided individually.Syntax
1 fun.call(thisArg[, arg1[, arg2[, ...]]])
来自javascript中的function.apply和function.call:
The apply() method is identical to call(), except apply() requires an
array as the second parameter. The array represents the arguments for
the target method.
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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 += 'output ' + position + '' + JSON.stringify(obj) + ' '; } 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' )); |
另见这把小提琴。
根本区别在于,
这是一个很小的帖子,我在上面写道:
http://sizeableeidea.com/call-versus-apply-javascript/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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" |
区别在于,
我们可以区分调用和应用方法,如下所示
call:带有参数的函数单独提供。如果知道要传递的参数,或者没有要传递的参数,则可以使用call。
应用:调用一个以数组形式提供参数的函数。如果不知道要传递给函数的参数有多少,则可以使用apply。
使用Apply over调用有一个优点,我们不需要更改参数的数目,只需要更改传递的数组。
性能差别不大。但是我们可以说调用比apply快一些,因为数组需要在apply方法中进行计算。
这些到方法的区别在于,您希望如何传递参数。
"A代表数组,C代表逗号"是一个方便的助记法。
调用和应用都用于在执行函数时强制执行
我在
1 2 3 4 5 6 7 8 9 10 | function sayHello() { console.log(this, arguments); } function hello() { sayHello.apply(this, arguments); } var obj = {name: 'my name'} hello.call(obj, 'some', 'arguments'); |
观察我们用
虽然
这里有一个函数允许您创建类,它还支持通过扩展其他类来创建类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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,我们可以更改作用域并正常传递参数,但是apply允许您使用参数作为数组来调用它(将它们作为数组传递)。但就它们在代码中的作用而言,它们非常相似。
While the syntax of this function is almost identical to that of
apply(), the fundamental difference is that call() accepts an argument
list, while apply() accepts a single array of arguments.
因此,正如您所看到的,没有什么大的区别,但仍然有一些情况我们更喜欢使用call()或apply()。例如,请看下面的代码,它使用apply方法从MDN中查找数组中最小和最大的数字:
1 2 3 4 5 6 7 8 9 | // 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) |
所以主要的区别就是我们传递参数的方式:呼叫:
1 | function.call(thisArg, arg1, arg2, ...); |
适用:
1 | function.apply(thisArg, [argsArray]); |
总结:
- 使用
call() 时,您必须正常地输入参数(从第二个参数开始) - 使用
apply() 时,必须传递参数数组。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |
在javascript中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 |