Питання Яка різниця між дзвінками та поданнями?


Яка різниця між використанням call і apply викликати функцію?

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

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

Чи існують відмінності в продуктивності між двома вищезазначеними методами? Коли це найкраще використовувати call над apply і навпаки?


2741
2017-12-31 19:56


походження


Думати про a в застосуванні для масиву args і c в дзвінку для стовпців args. - Larry Battle
@LarryBattle Повністю допоміг мені запам'ятати різницю для моєї 70-480 сертифікації :) - Shouvik
@LarryBattle Я роблю майже те ж саме, але я думаю, що в заявці для масиву та c в дзвінку через кому (тобто аргументи, розділені комами). - Samih
@LarryBattle приємний мнемонічний Ларрі, нарешті запам'ятав співвідношення з іменами - Tomo
ви застосовувати для роботи один раз (один аргумент), ви [телефон] дзвонити люди багато разів (кілька аргументів). Альтернатива: є [також?] Багато Зателефонувати з Duty ігри. - Gras Double


Відповіді:


Різниця в цьому apply дозволяє вам викликати функцію з arguments як масив; call вимагає зазначення параметрів явно. Потрібна мнемоніка "А. за array і С за сомма ".

Дивіться документацію MDN на застосовувати і дзвонити.

Псевдо-синтаксис:

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


3308
2017-12-31 20:00



Одна річ, щоб додати, що аргументи повинні бути числовими масивами ([]). Асоціативні масиви ({}) не працюватимуть. - Kevin Schroeder
@ KevinSchroeder: в ярлику JavaScript [] називається масив, {} називається об'єкт. - Martijn
Я часто забув, який приймає масив, і який сподівається, що ви перерахуєте аргументи. Техніка, яку я пам'ятаю, полягає в тому, якщо починається перша літера методу a то він приймає масив, тобто a плис масив - aziz punjani
@SAM Використання дзвонити замість звичайної функції виклик має сенс лише тоді, коли вам потрібно змінити значення це для виклику функції. Приклад (який перетворює аргументи-об'єкти в масив): Array.prototype.slice.call(arguments) або [].slice.call(arguments). застосовувати має сенс, якщо у вас є аргументи в масиві, наприклад, у функції, яка викликає іншу функцію з (майже) тими ж параметрами. Рекомендація Використовуйте звичайний виклик функції funcname(arg1) якщо це робить те, що вам потрібно, і збережіть дзвонити і застосовувати для тих особливих випадків, коли ви їх дійсно потребуєте. - some
@KunalSingh Обидва call і apply беремо два параметри. Перший аргумент apply' and функція call` повинна бути об'єктом власника, а другим параметром буде відповідно параметри масиву або коми. Якщо ви пройдете null або undefined як перший аргумент, то в нестрого режимі вони замінюються глобальним об'єктом, тобто window - A J Qarshi


К. Скотт Аллен має приємний запис з цього питання.

В принципі, вони відрізняються тим, як вони обробляють аргументи функції.

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

Так:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);

209
2017-12-31 19:59



другий параметр apply () і call () є необов'язковим, не обов'язковим. - angry kiwi
Перший параметр також не вимагається. - Ikrom


Щоб відповісти на частину про те, коли використовувати кожну функцію, скористайтеся 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 швидше, якщо ти це зробиш.


150
2017-12-31 21:50





Ось хороший мнемонік. А.Використовувати плівок А.раси і А.завжди приймає один-два Аргументи. Коли ви використовуєте Свсе, що вам потрібно Сount кількість аргументів.


102
2017-09-04 13:36



Корисно мнемонічний тут! Я зміню "один-два аргументи", щоб сказати "максимум два аргументи", оскільки ні перший, ні другий параметри apply необхідно. Я не впевнений, чому хтось зателефонує apply або call без параметра Схоже, хтось намагається з'ясувати, чому тут stackoverflow.com/questions/15903782/... - dantheta
Отже, коли використовувати Call () і коли використовувати Apply () .... - Krish


Хоча це стара тема, я просто хотів зазначити, що .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
2017-11-07 17:36



Це залежить від того, що функція виконує з параметрами / масивом, якщо для обробки масиву не потрібно, займає менше часу? - Relic
Цікаво, навіть без масиву, дзвінок все ще набагато швидше. jsperf.com/applyvscallvsfn2 - Josh Mc
@JoshMc Це було б дуже специфічним для браузера. У IE 11 я отримую заявку, що йде двічі швидше, ніж дзвінок. - Vincent McNabb
1. Створення нового масиву означає, що збирач сміття повинен очистити його в певний момент. 2. Доступ до елементів у масиві за допомогою dereference менш ефективний, ніж безпосередньо доступ до змінної (параметру). (Я вважаю, що це те, що має на увазі "колотень" під "розбору", що насправді є чимось зовсім іншим.) Але жоден з моїх аргументів не пояснює jsperf. Це має бути пов'язано з реалізацією двигуна обома функціями, наприклад У всякому разі, вони створюють порожній масив, якщо ніхто не був переданий. - joeytwiddle
Дякую, що поділилися тестом та відео - Gary


Випливає витяг з Закриття: остаточне керівництво Майкла Боліна. Це може виглядати трохи довго, але воно насичене, і багато розуміння. З "Додатку Б. Часто невірно зрозумілі поняття 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
2017-12-04 12:41



Просто зверніть увагу на те, що additionalValues не вказано всередині obj.addValues тіло - Viktor Stolbin
Я знаю, що ви відповіли на запитання, але хотіли б додати: ви можете використовувати bind при визначенні f. var f = obj.addValues; стає var f = obj.addValues.bind(obj)   і тепер f (20) буде працювати без необхідності використовувати дзвінок або застосовувати кожен раз. - jhliberty


Часом корисно часом, коли один об'єкт займає функцію іншого об'єкта, а це означає, що об'єкт запозичення просто виконує функцію позики так, як вона була її власною.

Невеликий приклад коду:

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
2018-02-25 19:31



Людям, які хочуть вміти бачити console.log перевірити: Що таке консоль.log і як я можу її використовувати? - Michel Ayres


Інший приклад з Call, Apply and Bind. Різниця між Call and Apply є очевидною, але Прив'язувати працює так:

  1. Прив'язка повертає екземпляр функції, яку можна виконати
  2. Перший параметр - "це'
  3. Другий параметр - це а Через кому список аргументів (наприклад Зателефонувати)

}

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
2018-03-31 07: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
2017-07-05 10:56