В чем разница между заявкой и заявкой?
В чем разница между использованием 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, существует также возможность ---- +: = 5 =: + ---- массив для использования с spread
Пример кода:
call
К. Скотт Аллен имеет хорошая статья по этому вопросу.
По сути, они отличаются тем, как они обрабатывают аргументы функции.
Метод apply () идентичен call (), за исключением того, что apply () требует массив в качестве второго параметра. Массив представляет аргументы для целевого метода. "
Итак:
// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
Чтобы ответить на часть о том, когда использовать каждую функцию, используйте 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
быстрее, если вы это сделаете.
Вот хорошая мнемоника. A pply использует A лучи, а A всегда принимает один или два аргумента. Когда вы используете C все, что вам нужно, C подсчитайте количество аргументов.
Хотя это старая тема, я просто хотел отметить, что .call немного быстрее, чем .apply. Я не могу сказать вам точно, почему.
См. jsPerf, http://jsperf.com/test-call-vs-apply. /3
[UPDATE!
]
Дуглас Крокфорд вкратце упоминает разницу между ними, что может помочь объяснить разницу в производительности ... http: //youtu.be/ya4UHuXNygM?t=15m52s
Apply принимает массив аргументов, а 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
при вызове функции. Когда null
или undefined
предоставляется как получатель для call()
или apply()
, глобального объект используется вместо значения для получателя. Поэтому предыдущий код имеет тот же нежелательный побочный эффект, что и добавление свойства с именем value
к глобальному объекту.
Может быть полезно думать о функции как о не имеющей знания о переменной, которой она назначена. Это помогает укрепить идею о том, что значение этого будет связано, когда функция вызывается, а не когда она определена.
Конец извлечения.
Иногда полезно, чтобы один объект заимствовал функцию другого объекта, а это означает, что объект заимствования просто выполняет одолженную функцию, как если бы она была его собственной.
Небольшой пример кода:
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 и 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
*/
Я хотел бы показать пример, где используется аргумент '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 () принимает аргументы через запятую, например:
.call(scope, arg1, arg2, arg3)
и apply () принимает массив аргументов, например:
.apply(scope, [arg1, arg2, arg3])
вот еще несколько примеров использования: http://blog.i-evaluation.com/2012/08/15 /JavaScript-колл-и применить /
Из документов 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()
принимает список аргументов , а ---- +: = 1 =: + ---- принимает один массив аргументов .
Вот небольшой пост, о котором я писал:
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: функция с аргументом предоставляет индивидуально. Если вы знаете аргументы для передачи или нет аргументов для передачи, вы можете использовать call.
APPLY: вызов функции с аргументом в виде массива. Вы можете использовать apply, если не знаете, сколько аргументов будет передано функции.
Есть преимущество использования apply over call, нам не нужно изменять количество аргументов, только мы можем изменить переданный массив.
В производительности нет большой разницы. Но мы можем сказать, что вызов немного быстрее, чем сравнение для применения, потому что массив должен оцениваться в методе apply.
Разница между этими методами заключается в том, как вы хотите передать параметры.
«A для массива и C для запятой» - удобная мнемоника.
Вызов и применение обоих используются для принудительного использования значения 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
Этого очень трудно достичь.
Несмотря на то, что 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()
методы, которые расположены в 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