提问人:John Duff 提问时间:1/1/2010 最后编辑:MikeJohn Duff 更新时间:10/7/2023 访问量:812226
call 和 apply 有什么区别?
What is the difference between call and apply?
问:
使用函数和调用函数有什么区别?Function.prototype.apply()
Function.prototype.call()
const func = function() {
alert("Hello world!");
};
func.apply()
HSP的func.call()
上述两种方法之间是否存在性能差异?什么时候最好使用,反之亦然?call
apply
答:
K·斯科特·艾伦(K. Scott Allen)对此事有一篇很好的文章。
基本上,它们在处理函数参数的方式上有所不同。
apply() 方法与 call() 相同,但 apply() 需要数组作为第二个参数。数组表示目标方法的参数。
所以:
// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
评论
call
apply
不同之处在于,它允许您以 as 数组的形式调用函数; 需要显式列出参数。一个有用的助记词是“A 代表 rray,C 代表 comma”。apply
arguments
call
伪语法:
theFunction.apply(valueForThis, arrayOfArgs)
theFunction.call(valueForThis, arg1, arg2, ...)
从 ES6 开始,还可以扩展
数组以与该函数一起使用,您可以在此处查看兼容性。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
评论
[]
{}
Array.prototype.slice.call(arguments)
[].slice.call(arguments)
funcname(arg1)
call
apply
apply' and
null
undefined
window
要回答有关何时使用每个函数的部分,如果您不知道将要传递的参数数量,或者它们已经在数组或类似数组的对象中(例如转发您自己的参数的对象),请使用。否则使用,因为无需将参数包装在数组中。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
虽然这是一个古老的话题,但我只是想指出 .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...)
评论
我想举一个例子,其中使用了“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*
这是一个很好的助记符。一个pply 使用 A rrays,A lways 需要一个或两个参数。当你使用 C时,你必须控制参数的数量。
评论
apply
apply
call
我们可以区分调用和应用方法,如下所示
CALL :一个带有参数的函数,单独提供。 如果您知道要传递的参数,或者没有要传递的参数,则可以使用 call。
APPLY :调用一个函数,参数以数组形式提供。如果您不知道有多少参数将传递给函数,则可以使用 apply。
使用apply而不是调用有一个优点,我们不需要更改参数的数量,只有我们可以更改传递的数组。
性能差别不大。但是我们可以说调用比 apply 快一点,因为数组需要在 apply 方法中计算。
摘自迈克尔·博林(Michael Bolin)的《闭合:权威指南》(Closure: The Definitive Guide)。它可能看起来有点冗长,但它充满了很多见解。摘自“附录 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.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]);
但是,由于在未指定 receiver 参数时,nor 使用其自己的 receiver 的值来替换 receiver 参数,因此以下操作将不起作用: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
将函数视为不知道它所分配的变量可能会有所帮助。这有助于强化这样一种观点,即在调用函数时,而不是在定义函数时,将绑定此值。
提取结束。
评论
additionalValues
obj.addValues
var f = obj.addValues;
var f = obj.addValues.bind(obj)
这是一篇很小的帖子,我写过:
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"
评论
Call() 采用逗号分隔的参数,例如:
.call(scope, arg1, arg2, arg3)
apply() 接受一个参数数组,例如:
.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()
评论
即使实现同样的事情,我认为至少有一个地方你不能使用,但只能使用.也就是说,当您想要支持继承并想要调用构造函数时。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
评论
另一个示例是 Call、Apply 和 Bind。 Call 和 Apply 之间的区别很明显,但 Bind 的工作原理是这样的:
- Bind 返回可执行的函数的实例
- 第一个参数是 'this'
- 第二个参数是逗号分隔的参数列表(如 Call)
}
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
*/
这些方法之间的区别在于,您希望如何传递参数。
“A 表示数组,C 表示逗号”是一个方便的助记符。
评论
调用和应用都用于在执行函数时强制执行该值。唯一的区别是接受 1 为 和 的参数。 只取两个参数,一个是参数数组,一个是参数数组。this
call
n+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
来自 Function.prototype.apply() 上的 MDN 文档:
apply() 方法调用具有给定值的函数,并且 参数以数组(或类似数组的对象)形式提供。
this
语法
fun.apply(thisArg, [argsArray])
来自 Function.prototype.call() 上的 MDN 文档:
call() 方法调用具有给定值和单独提供的参数的函数。
this
语法
fun.call(thisArg[, arg1[, arg2[, ...]]])
从 JavaScript 中的 Function.apply 和 Function.call :
apply() 方法与 call() 相同,但 apply() 需要 array 作为第二个参数。数组表示 目标方法。
代码示例:
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,我们可以像往常一样更改范围并传递参数,但 apply 允许您使用参数作为数组来调用它(将它们作为数组传递)。但就它们在代码中要执行的操作而言,它们非常相似。
虽然此函数的语法几乎与 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]);
总结:
两者都是位于 上的方法。因此,它们可以通过原型链在每个函数对象上使用。两者都可以执行指定值为 的函数。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
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
只有一个细微的区别:
- 操作员...允许将 Iterable 作为要调用的列表传递。
spread
args
- 只接受类似数组的参数。
apply
因此,这些呼吁是相辅相成的。我们期望一个可迭代的地方,工作,在我们期望一个类似数组的地方,工作。call
apply
对于既可迭代又类似数组的对象,比如真正的数组,从技术上讲,我们可以使用其中任何一个,但应用可能会更快,因为大多数 JavaScript 引擎在内部优化得更好。
我只想在flatline的一篇解释良好的帖子中添加一个简单的例子,这让初学者很容易理解。
func.call(context, args1, args2 ); // pass arguments as "," separated value
func.apply(context, [args1, args2]); // pass arguments as "Array"
我们还使用“Call”和“Apply”方法来更改引用,如下面的代码中所定义
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 object
Emp1.getEmpDetail.apply(Emp3, [30, 'Admin'])
该方法调用具有给定值和第二个参数的函数,这些参数是用逗号分隔的参数call()
this
object.someMethod.call( someObject, arguments )
该方法与 call 相同,只是它采用的第二个参数是参数数组apply()
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
在 apply() 方法中,参数以数组的形式调用,而在 call() 方法中,参数作为单独的参数单独传递
在性能方面,调用比 apply() 方法快一点,因为该方法中没有数组参数。
使用 apply() 方法,当您有类似数组的对象或实际数组时。 使用 call() 方法,当您知道参数的确切数量时,您希望传递给函数。
评论
a
c
args
...
fn.apply(context, args)
fn.call(context, ...args)