В чем разница между использованием call
и apply
для вызова функции?
var func = function() {
alert('hello!');
};
func.apply();
vs func.call();
Существуют ли различия в производительности между двумя вышеупомянутыми методами? Когда лучше использовать call
over apply
и наоборот?
Разница заключается в том, что apply
позволяет вызывать функцию с arguments
как массив; call
требует, чтобы параметры были перечислены явно. Полезная мнемоника " A для a rray и C для c omma."
См. документацию MDN на apply и call.
Псевдо-синтаксис:
theFunction.apply(valueForThis, arrayOfArgs)
theFunction.call(valueForThis, arg1, arg2, ...)
Существует также, с ES6, возможность spread
массива для использования с функцией 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
call()
было чем-то вроде IE.
К. Скотт Аллен хорошая запись по этому вопросу.
В основном, они отличаются тем, как они обрабатывают аргументы функции.
Метод apply() идентичен вызову(), за исключением того, что apply() требует, чтобы в качестве второго параметра использовался массив. Массив представляет аргументы для целевого метода.
Итак:
// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
Чтобы ответить на вопрос о том, когда использовать каждую функцию, используйте apply
, если вы не знаете числа аргументов, которые вы передадите, или если они уже находятся в массиве или подобном массиву объекте (например, t21 > object для пересылки ваших собственных аргументов. Используйте call
иначе, так как нет необходимости обертывать аргументы в массиве.
f.call(thisObject, a, b, c); // Fixed number of arguments
f.apply(thisObject, arguments); // Forward this function 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
быстрее, если вы это сделаете.
Здесь хорошая мнемоника. A pply использует A rrays и A всегда принимает один или два аргумента. Когда вы используете C все, что вам нужно C, укажите количество аргументов.
apply
. Я не уверен, хотя, почему один будет вызывать apply
или call
без параметра. Похоже, кто-то пытается выяснить, почему здесь stackoverflow.com/questions/15903782/…
Пока это старая тема, я просто хотел указать, что .call немного быстрее, чем .apply. Я не могу точно сказать, почему.
См. jsPerf, http://jsperf.com/test-call-vs-apply/3
[UPDATE!
]
Дуглас Крокфорд кратко упоминает разницу между ними, что может помочь объяснить разницу в производительности... http://youtu.be/ya4UHuXNygM?t=15m52s
Применить принимает массив аргументов, в то время как Call принимает нулевые или более отдельные параметры! Ах, ха!
.apply(this, [...])
.call(this, param1, param2, param3, param4...)
Далее следует выдержка из книги "Закрытие: полное руководство" Майкла Болина. Это могло бы выглядеть немного длинным, но это пропитало много понимания. Из "Приложения Б. Часто неверно понимаемые концепции 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
при вызове функции. Когда в качестве получателя для call()
или apply()
null
или undefined
вместо него используется глобальный объект. Поэтому предыдущий код имеет тот же нежелательный побочный эффект, что и добавление свойства с именем value
к глобальному объекту.
Может быть полезно думать о функции как о не имеющей знания о переменной, которой она назначена. Это помогает укрепить идею о том, что значение этого будет связано, когда функция вызывается, а не когда она определена.
Конец выписки.
additionalValues
не ссылается внутри obj.addValues
тела
var f = obj.addValues;
становится var f = obj.addValues.bind(obj)
и теперь f (20) будет работать без необходимости каждый раз использовать call или 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
Эти методы очень полезны для предоставления временных объектов объектов.
console.log
посмотрите: что такое console.log и как его использовать?
Другой пример с 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
*/
Я хотел бы показать пример, где используется аргумент 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 *
Call() принимает разделенные запятыми аргументы, ex:
.call(scope, arg1, arg2, arg3)
и apply() принимает массив аргументов, ex:
.apply(scope, [arg1, arg2, arg3])
Вот несколько примеров использования: http://blog.i-evaluation.com/2012/08/15/javascript-call-and-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() идентичен вызову(), за исключением того, что 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()
принимает список аргументов , а 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()
принимает аргументы функции в массиве.
Мы можем дифференцировать методы вызова и применения, как показано ниже
CALL: функция с аргументом предоставляется индивидуально. Если вы знаете аргументы, которые должны быть переданы, или нет аргументов для передачи, вы можете использовать вызов.
APPLY: вызов функции с аргументом, представленным в виде массива. Вы можете использовать apply, если вы не знаете, сколько аргументов будет передано функции.
Существует преимущество использования приложения over call, нам не нужно изменять количество аргументов, мы можем изменить передаваемый массив.
В производительности нет большой разницы. Но мы можем сказать, что вызов бит быстрее, чем сравнение, потому что массив должен оцениваться в методе apply.
Вызов и применение обоих используются для принудительного значения this
при выполнении функции. Единственное отличие состоит в том, что call
принимает аргументы n+1
, где 1 - this
и 'n' arguments
. apply
принимает только два аргумента, один - this
, другой - массив аргументов.
Преимущество, которое я вижу в apply
over 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
и 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
Основное отличие заключается в том, что с помощью вызова мы можем изменить область и передать аргументы как обычно, но применять позволяет вам вызывать его, используя аргументы в качестве массива (передать их в виде массива). Но с точки зрения того, что они должны делать в вашем коде, они довольно похожи.
Хотя синтаксис этой функции практически идентичен синтаксису метода 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
a
in для массива аргументов иc
при вызове столбцов аргументов.