В чем разница между призывом и подачей заявки?

В чем разница между использованием call и apply для вызова функции?

var func = function() {
  alert('hello!');
};

func.apply(); vs func.call();

Существуют ли различия в производительности между двумя вышеупомянутыми методами? Когда лучше использовать call over apply и наоборот?

2754
задан John Duff 31 дек. '10 в 22:56
источник поделиться

19 ответов

Разница заключается в том, что 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
3322
ответ дан flatline 31 дек. '10 в 23:00
источник поделиться

К. Скотт Аллен хорошая запись по этому вопросу.

В основном, они отличаются тем, как они обрабатывают аргументы функции.

Метод apply() идентичен вызову(), за исключением того, что apply() требует, чтобы в качестве второго параметра использовался массив. Массив представляет аргументы для целевого метода.

Итак:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
210
ответ дан notnoop 31 дек. '10 в 22:59
источник поделиться

Чтобы ответить на вопрос о том, когда использовать каждую функцию, используйте 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 быстрее, если вы это сделаете.

150
ответ дан Matthew Crumley 01 янв. '10 в 0:50
источник поделиться

Здесь хорошая мнемоника. A pply использует A rrays и A всегда принимает один или два аргумента. Когда вы используете C все, что вам нужно C, укажите количество аргументов.

102
ответ дан Joe 04 сент. '13 в 16:36
источник поделиться

Пока это старая тема, я просто хотел указать, что .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...)

91
ответ дан kmatheny 07 нояб. '11 в 20:36
источник поделиться

Выполняет выдержку из Закрытие: окончательное руководство Майкла Болина. Это может показаться немного длинным, но оно пропитано большим пониманием. Из "Приложение 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);

Если при вызове функции нет явного приемника, глобальный объект становится получателем. Как поясняется в "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 в глобальный объект.

Может быть полезно подумать о том, что функция не знает о переменной, которой она назначена. Это помогает укрепить идею о том, что значение этого будет связано при вызове функции, а не при ее определении.


Конец извлечения.

71
ответ дан Dominykas Mostauskis 04 дек. '13 в 15:41
источник поделиться

Иногда полезно, чтобы один объект заимствовал функцию другого объекта, а это означает, что объект заимствования просто выполняет функцию предоставления кредита, как если бы она была собственной.

Пример небольшого кода:

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

Эти методы очень полезны для предоставления временных объектов объектов.

33
ответ дан tjacks3 25 февр. '14 в 22:31
источник поделиться

Другой пример с 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
*/
23
ответ дан Mahesh 31 марта '15 в 10:32
источник поделиться

Я хотел бы показать пример, где используется аргумент 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 *

21
ответ дан user669677 05 июля '13 в 13:56
источник поделиться

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/

20
ответ дан Mark Karwowski 21 янв. '14 в 21:11
источник поделиться

Из документы 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'
));

См. также этот скрипт.

18
ответ дан John Slegers 18 янв. '16 в 5:27
источник поделиться

Основное отличие состоит в том, что call() принимает список аргументов , а apply() принимает один массив аргументов.

11
ответ дан Rakesh Kumar 28 февр. '14 в 7:57
источник поделиться

Здесь небольшая почта, я написал об этом:

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"
10
ответ дан Dan 04 дек. '13 в 16:58
источник поделиться

Разница в том, что call() принимает аргументы функции отдельно, а apply() принимает аргументы функции в массиве.

7
ответ дан Sanjib Debnath 11 авг. '16 в 12:25
источник поделиться

Мы можем дифференцировать методы вызова и применения, как показано ниже

CALL: функция с аргументом предоставляется индивидуально. Если вы знаете аргументы, которые должны быть переданы, или нет аргументов для передачи, вы можете использовать вызов.

APPLY: вызов функции с аргументом, представленным в виде массива. Вы можете использовать apply, если вы не знаете, сколько аргументов будет передано функции.

Существует преимущество использования приложения over call, нам не нужно изменять количество аргументов, мы можем изменить передаваемый массив.

В производительности нет большой разницы. Но мы можем сказать, что вызов бит быстрее, чем сравнение, потому что массив должен оцениваться в методе apply.

6
ответ дан Praveen D 06 нояб. '13 в 14:50
источник поделиться

Разница между этими методами заключается в том, как вы хотите передать параметры.

"A для массива и C для запятой" - это удобная мнемоника.

5
ответ дан venkat7668 03 авг. '15 в 9:15
источник поделиться

Вызов и применение обоих используются для принудительного значения 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 это очень трудно достичь.

5
ответ дан Raghavendra 24 нояб. '15 в 11:36
источник поделиться

Даже если 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
4
ответ дан Dhana Krishnasamy 12 февр. '15 в 21:10
источник поделиться

Основное различие заключается в том, что с помощью вызова мы можем изменить область действия и передать аргументы как обычно, но применить это позволяет вызывать ее с использованием аргументов в виде массива (передать их как массив). Но с точки зрения того, что они делают в вашем коде, они довольно похожи.

Хотя синтаксис этой функции почти идентичен силе 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]);
2
ответ дан Alireza 11 мая '17 в 17:35
источник поделиться

Другие вопросы по меткам