使用call
和apply
调用函数有什么区别?
var func = function() {
alert('hello!');
};
func.apply();
vs func.call();
上述两种方法之间是否存在性能差异?何时最好使用call
而不是apply
,反之亦然?
答案 0 :(得分:3472)
不同之处在于apply
允许您使用arguments
作为数组调用函数; call
要求显式列出参数。一个有用的助记符是“ A a rray和 C c omma。”
伪语法:
theFunction.apply(valueForThis, arrayOfArgs)
theFunction.call(valueForThis, arg1, arg2, ...)
从ES6开始,还有spread
数组用于call
函数的可能性,您可以看到兼容性here。
示例代码:
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
答案 1 :(得分:218)
ķ。斯科特艾伦有a nice writeup的问题。
基本上,它们在处理函数参数方面有所不同。
apply()方法与call()相同,但apply()需要数组作为第二个参数。数组表示目标方法的参数。“
所以:
// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
答案 2 :(得分:152)
要回答有关何时使用每个函数的部分,如果您不知道要传递的参数数量,或者它们是否已经在数组或类数组对象中,请使用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
会更快。
答案 3 :(得分:105)
这是一个很好的助记符。 A pply使用 A 数组, A 总是需要一个或两个参数。当你使用 C 时,你必须 C 计算参数的数量。
答案 4 :(得分:91)
虽然这是一个古老的话题,但我只想指出.call比.apply略快。我无法确切地告诉你原因。
参见jsPerf,http://jsperf.com/test-call-vs-apply/3
[UPDATE!
]
Apply接受一组参数,而Call接受零个或多个单独的参数!啊哈!
.apply(this, [...])
.call(this, param1, param2, param3, param4...)
答案 5 :(得分:75)
关注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”中所述,窗口是在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()
都没有使用其自己的接收器的值来替换未指定的接收器参数,因此以下内容不起作用:
// 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
的属性的不良副作用。
将函数视为不知道它所分配的变量可能会有所帮助。这有助于强化这样一种观点,即在调用函数时它将被绑定,而不是在定义它时。
提取物结束。
答案 6 :(得分:33)
有时一个对象借用另一个对象的函数很有用,这意味着借用对象只是执行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
这些方法对于为对象提供临时功能非常有用。
答案 7 :(得分:25)
Call,Apply和Bind的另一个例子。 Call和Apply之间的区别很明显,但 Bind 的工作原理如下:
}
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
*/
答案 8 :(得分:23)
我想展示一个例子,其中使用'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]
答案 9 :(得分:20)
Call()采用逗号分隔的参数,例如:
.call(scope, arg1, arg2, arg3)
和apply()接受一组参数,例如:
.apply(scope, [arg1, arg2, arg3])
这里有几个用法示例: http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/
答案 10 :(得分:19)
来自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]]);
}
}
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'
));
另见this Fiddle。
答案 11 :(得分:11)
基本区别在于call()
接受 参数列表 ,而apply()
接受 单个参数数组< / EM> 强>
答案 12 :(得分:10)
这是一篇小帖子,我在上面写道:
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"
答案 13 :(得分:7)
不同之处在于call()
分别获取函数参数,apply()
获取数组中的函数参数。
答案 14 :(得分:6)
我们可以区分呼叫和应用方法,如下所示
CALL:具有参数的函数单独提供。 如果你知道要传递的参数或者没有传递参数,你可以使用call。
APPLY:使用作为数组提供的参数调用函数。如果您不知道有多少参数将传递给函数,则可以使用apply。
使用apply over call有一个优点,我们不需要更改参数的数量,只有我们可以更改传递的数组。
性能没有太大差异。但是我们可以说调用比应用更快一点,因为数组需要在apply方法中进行评估。
答案 15 :(得分:5)
这些方法之间的区别在于,您希望如何传递参数。
“A代表数组,C代表逗号”是一个方便的助记符。
答案 16 :(得分:5)
调用和应用两者用于在执行函数时强制this
值。唯一的区别是call
需要n+1
个参数,其中1是this
和'n' arguments
。 apply
只接受两个参数,一个是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');
观察我们使用hello
轻松将sayHello
委托给apply
,但使用call
这很难实现。
答案 17 :(得分:4)
尽管call
和apply
实现同样的目标,但我认为至少有一个地方您无法使用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
答案 18 :(得分:4)
主要区别在于,使用call,我们可以正常更改范围和传递参数,但apply允许您使用参数作为Array调用它(将它们作为数组传递)。但就你在代码中的作用而言,它们非常相似。
虽然这个函数的语法几乎与 apply(),根本区别在于call()接受一个参数 list,而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]);
答案 19 :(得分:3)
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
在JavaScript中,this
值有时可能很棘手。 {strong>在执行函数时而不是在定义函数时确定的值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
答案 20 :(得分:1)
让我为此添加一些细节。
这两个电话几乎相等:
func.call(context, ...args); // pass an array as list with spread operator
func.apply(context, args); // is same as using apply
只有很小的区别:
spread
运算符...允许传递 可迭代args
作为要调用的列表。apply
仅接受 类数组 args。
因此,这些调用相互补充。在我们期望可迭代的地方,call
起作用,在我们期望类似数组的,apply
起作用。
对于可迭代和类似于数组的对象,就像真实数组一样,从技术上讲,我们可以使用其中的任何对象,但 可能会更快,因为大多数JavaScript引擎在内部对其进行了优化。
答案 21 :(得分:1)
call() 它是 javascript 中的预定义方法。 此方法通过指定所有者对象来调用方法(函数)。
function sayHello(){
return "Hello " + this.name;
}
var obj = {name: "Sandy"};
sayHello.call(obj);
// Returns "Hello Sandy"
调用接受参数
function saySomething(message){
return this.name + " is " + message;
}
var person4 = {name: "John"};
saySomething.call(person4, "awesome");
// Returns "John is awesome"
apply() apply 方法类似于 call() 方法。唯一不同的是, call() 方法分别接受参数,而 apply() 方法将参数作为数组。
示例
function saySomething(message){
return this.name + " is " + message;
}
var person4 = {name: "John"};
saySomething.apply(person4, ["awesome"]);
答案 22 :(得分:0)
由flatline解释的很好。我只想添加一个简单的示例。这对于初学者来说很容易理解。
func.call(context, args1 , args2 ); // pass arguments as "," saprated value
func.apply(context, [args1 , args2 ]); // pass arguments as "Array"
我们还使用“调用”和“应用”方法来更改以下代码中定义的引用
let Emp1 = {
name: 'X',
getEmpDetail: function (age, department) {
console.log('Name :', this.name, ' Age :', age, ' Department :', department)
}
}
Emp1.getEmpDetail(23, 'Delivery')
// 1st approch of chenging "this"
let Emp2 = {
name: 'Y',
getEmpDetail: Emp1.getEmpDetail
}
Emp2.getEmpDetail(55, 'Finance')
// 2nd approch of changing "this" using "Call" and "Apply"
let Emp3 = {
name: 'Z',
}
Emp1.getEmpDetail.call(Emp3, 30, 'Admin')
// here we have change the ref from **Emp1 to Emp3** object
// now this will print "Name = X" because it is pointing to Emp3 object
Emp1.getEmpDetail.apply(Emp3, [30, 'Admin']) //
答案 23 :(得分:0)
apply()
-
与call()
方法类似,apply()
方法中的第一个参数设置this
值,该值是在其上调用函数的对象。在这种情况下,它就是上面的obj
对象。 apply()
和call()
方法之间的唯一区别是apply()
方法的第二个参数将实际函数的参数接受为数组。
答案 24 :(得分:0)
以相同的方式调用和应用。当我们使用call and apply时,它将立即调用。
调用和应用都将“ this”参数作为第一个参数,而第二个参数只是不同。
该调用将函数的参数作为列表(逗号) Apply将函数的参数作为数组。
您可以在下面的youtube视频中找到绑定,调用和应用之间的完全区别。