В чем разница между использованием call
и apply
для вызова функции?
var func = function() {
alert('hello!');
};
func.apply();
против func.call();
Есть ли различия в производительности между двумя вышеупомянутыми методами? Когда лучше использовать call
над apply
и наоборот?
19 ответов
Разница в том, что apply
позволяет вам вызывать функцию с arguments
в виде массива; call
требует явного перечисления параметров. Полезной мнемоникой является " A для a Рэй и C для c омма."
См. Документацию MDN по apply и call.
Псевдосинтаксис:
theFunction.apply(valueForThis, arrayOfArgs)
theFunction.call(valueForThis, arg1, arg2, ...)
Начиная с ES6, существует также возможность {{X0} } массив для использования с функцией 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
Позвольте мне добавить немного деталей к этому.
Эти два вызова почти эквивалентны:
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
работает.
А для объектов, которые являются итерируемыми и массивоподобными , как реальный массив, технически мы могли бы использовать любой из них, но apply , вероятно, будет быстрее , потому что большинство движков JavaScript внутренне оптимизируют его лучше.
Основное отличие заключается в том, что с помощью call мы можем изменить область видимости и передать аргументы в обычном режиме, но метод apply позволяет вам вызывать его, используя аргументы в качестве массива (передавая их как массив). Но с точки зрения того, что они должны делать в вашем коде, они довольно похожи.
Хотя синтаксис этой функции почти идентичен синтаксису apply (), фундаментальное отличие состоит в том, что call () принимает список аргументов, а apply () принимает единственный массив аргументов.
Итак, как видите, нет большой разницы, но все же есть случаи, когда мы предпочитаем использовать call () или apply (). Например, посмотрите на приведенный ниже код, который находит наименьшее и наибольшее число в массиве из MDN, используя метод apply:
// 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
Зачем мне нужно использовать эти функции?
Значение 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
Хотя 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
Вызов и применение обоих используются для принудительного использования значения 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
этого очень трудно достичь.
Разница между этими методами заключается в том, как вы хотите передать параметры.
«A для массива и C для запятой» - удобная мнемоника.
Мы можем дифференцировать вызов и применять методы, как показано ниже
CALL: функция с аргументом обеспечивает индивидуально. Если вы знаете аргументы для передачи или нет аргументов для передачи, вы можете использовать call.
APPLY: вызов функции с аргументом, представленным в виде массива. Вы можете использовать apply, если вы не знаете, сколько аргументов будет передано функции.
Есть преимущество использования apply over call, нам не нужно изменять количество аргументов, только мы можем изменить передаваемый массив.
Там нет большой разницы в производительности. Но мы можем сказать, что вызов немного быстрее, чем сравнение для применения, потому что массив должен оцениваться в методе apply.
Разница в том, что call()
принимает аргументы функции отдельно, а apply()
принимает аргументы функции в массиве.
Вот небольшой пост, я написал об этом:
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()
принимает список аргументов , а apply()
принимает один массив аргументов .
Из документов MDN для Function.prototype. .apply () :
Метод apply () вызывает функцию с заданным значением
this
и аргументы предоставляются в виде массива (или объекта, похожего на массив).Синтаксис
fun.apply(thisArg, [argsArray])
Из документов MDN для Function.prototype. .call () :
Метод call () вызывает функцию с заданным значением
this
и аргументами, предоставляемыми индивидуально.Синтаксис
fun.call(thisArg[, arg1[, arg2[, ...]]])
С Function.apply и Function.call в 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'
));
См. Также эту скрипку .
Call () принимает аргументы, разделенные запятыми, например:
.call(scope, arg1, arg2, arg3)
И apply () принимает массив аргументов, например:
.apply(scope, [arg1, arg2, arg3])
Вот еще несколько примеров использования: http://blog.i-evaluation.com/2012/08/15 / JavaScript - вызова и применить /
Я хотел бы показать пример, где используется аргумент 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а> *
Иногда одному объекту полезно заимствовать функцию другого объекта, а это означает, что объект-заемщик просто выполняет одолженную функцию, как если бы она была его собственной.
Небольшой пример кода:
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
Эти методы очень полезны для придания объектам временной функциональности.
Далее следует выдержка из Закрытие: полное руководство Майкла Болина. Это может выглядеть немного длинным, но оно насыщено глубоким пониманием. Из «Приложения Б. Часто неверно понимаемые концепции 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);
Если нет явного получателя при вызове функции, тогда глобальный объект становится получателем. Как объясняется в «goog.global» на странице 47, окно - это глобальный объект, когда 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
к глобальному объекту.
Может быть полезно думать о функции как о не имеющей знания о переменной, которой она назначена. Это помогает укрепить идею о том, что значение этого будет связано, когда функция вызывается, а не когда она определена.
Конец выписки.
Хотя это старая тема, я просто хотел отметить, что .call немного быстрее, чем .apply. Я не могу сказать вам точно, почему.
См. JsPerf, http://jsperf.com/test-call-vs-apply/3 а>
[ UPDATE!
]
Дуглас Крокфорд кратко упоминает разницу между ними, что может помочь объяснить разницу в производительности ... http: // youtu .be / ya4UHuXNygM ? Т = 15m52s
Apply принимает массив аргументов, а Call принимает ноль или более отдельных параметров! Ах, ха!
.apply(this, [...])
.call(this, param1, param2, param3, param4...)
Чтобы ответить на часть о том, когда использовать каждую функцию, используйте apply
, если вы не знаете, сколько аргументов вы будете передавать, или если они уже находятся в массиве или массивоподобном объекте (например, {{ X1}} объект для пересылки ваших собственных аргументов. В противном случае используйте 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
быстрее, если у вас есть.
К. Скотт Аллен имеет хорошая статья по этому вопросу.
По сути, они отличаются тем, как они обрабатывают аргументы функции.
Метод apply () идентичен call (), за исключением того, что apply () требует массив в качестве второго параметра. Массив представляет аргументы для целевого метода. "
Так:
// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
Похожие вопросы
Связанные вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript/JS) и его различных диалектах/реализациях (кроме ActionScript). Имейте в виду, что JavaScript — это НЕ то же самое, что Java! Включите все ярлыки, относящиеся к вашему вопросу; например, [node.js], [jQuery], [JSON], [ReactJS], [angular], [ember.js], [vue.js], [typescript], [svelte] и т. д.