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

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

func.apply(); против func.call();

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

3258
John Duff 31 Дек 2009 в 22:56

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
3614
flatline 19 Сен 2019 в 21:11

Позвольте мне добавить немного деталей к этому.

Эти два вызова почти эквивалентны:

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 внутренне оптимизируют его лучше.

2
Pravin Divraniya 12 Сен 2019 в 07:38

Основное отличие заключается в том, что с помощью 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]);
4
Alireza 26 Янв 2019 в 13:13

Резюме:

И 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
4
Willem van der Veen 29 Авг 2018 в 15:29

Хотя 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 Фев 2015 в 18:10

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

5
Raghavendra 24 Ноя 2015 в 08:36

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

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

5
venkat7668 3 Авг 2015 в 06:15

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

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

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

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

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

6
Praveen D 6 Ноя 2013 в 11:50

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

7
Hkachhia 28 Ноя 2017 в 06:41

Вот небольшой пост, я написал об этом:

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 4 Дек 2013 в 13:58

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

11
Rakesh Kumar 28 Фев 2014 в 04:57

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

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

19
John Slegers 20 Июн 2016 в 17:06

Call () принимает аргументы, разделенные запятыми, например:

.call(scope, arg1, arg2, arg3)

И apply () принимает массив аргументов, например:

.apply(scope, [arg1, arg2, arg3])

Вот еще несколько примеров использования: http://blog.i-evaluation.com/2012/08/15 / JavaScript - вызова и применить /

20
Dean Radcliffe 25 Янв 2014 в 16:40

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

23
16 Янв 2014 в 12:47

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

Небольшой пример кода:

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

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

34
tjacks3 25 Фев 2014 в 19:31

Далее следует выдержка из Закрытие: полное руководство Майкла Болина. Это может выглядеть немного длинным, но оно насыщено глубоким пониманием. Из «Приложения Б. Часто неверно понимаемые концепции 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 к глобальному объекту.

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


Конец выписки.

76
Dominykas Mostauskis 24 Май 2014 в 16:15

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

92
kmatheny 18 Янв 2014 в 17:44

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

158
Matthew Crumley 31 Дек 2009 в 21:56

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

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

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

Так:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
227
neoswf 19 Июл 2012 в 20:53