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

2693

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

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

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

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

  • 702
    Подумайте о a in для массива аргументов и c при вызове столбцов аргументов.
  • 23
    @LarryBattle Полностью помог мне вспомнить разницу для моей сертификации 70-480 :)
Показать ещё 12 комментариев
Теги:
performance
dynamic
function

20 ответов

3398
Лучший ответ

Разница заключается в том, что 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
  • 0
    Они оба поддерживаются большинством браузеров? Кажется, я помню, что чтение call() было чем-то вроде IE.
  • 22
    Следует добавить, что аргументы должны быть числовым массивом ([]). Ассоциативные массивы ({}) не будут работать.
Показать ещё 9 комментариев
224

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

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

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

Итак:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
  • 41
    второй параметр apply () и call () является необязательным, необязательным.
  • 33
    Первый параметр тоже не требуется.
144

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

108

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

  • 2
    Полезная мнемоника тут же! Я изменю «один или два аргумента» на «максимум два аргумента», поскольку не требуется ни первый, ни второй параметры apply . Я не уверен, хотя, почему один будет вызывать apply или call без параметра. Похоже, кто-то пытается выяснить, почему здесь stackoverflow.com/questions/15903782/…
  • 0
    Итак, когда использовать Call () и когда использовать Apply () ....
95

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

  • 0
    Это зависит от того, что функция делает с параметрами / массивом, если ей не нужно обрабатывать массив, это займет меньше времени?
  • 11
    Интересно, что даже без массива, вызов по-прежнему намного быстрее. jsperf.com/applyvscallvsfn2
Показать ещё 3 комментария
71

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

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


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

  • 0
    Просто к сведению тот факт, что additionalValues не ссылается внутри obj.addValues тела
  • 0
    Я знаю, что вы отвечали на вопрос, но хотели бы добавить: вы могли бы использовать bind при определении f. var f = obj.addValues; становится var f = obj.addValues.bind(obj) и теперь f (20) будет работать без необходимости каждый раз использовать call или apply.
32

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

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

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

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

23

Другой пример с 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
*/
21

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

19

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/

  • 0
    `// call () === аргументы, разделенные запятыми (список аргументов) .call (this, args1, args2, args3, ...) // apply () === массив аргументов (array-items). применить (это, [arr0, arr1, arr2, ...]) `
18

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

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

11

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

10

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

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"
7

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

6

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

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

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

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

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

5

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

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

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

  • 10
    Что дает этот ответ, который еще недостаточно хорошо представлен в других ответах?
4

Даже если 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
  • 0
    Я считаю, что вызов будет работать там с оператором распространения, как описано в выбранном ответе. Если я что-то упустил.
3

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

Хотя синтаксис этой функции практически идентичен синтаксису метода 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

Резюме:

Оба метода 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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню