Pytanie Jaka jest różnica między połączeniem i zastosowaniem?


Jaka jest różnica między używaniem call i apply wywołać funkcję?

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

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

Czy występują różnice w wydajności między dwoma wyżej wymienionymi metodami? Kiedy najlepiej używać call koniec apply i wzajemnie?


2741
2017-12-31 19:56


pochodzenie


Myśleć o a stosuje się do tablicy argumentów i c w wywołaniu kolumn argumentów. - Larry Battle
@LarryBattle Całkowicie pomógł mi pamiętać różnicę w moim certyfikacie 70-480 :) - Shouvik
@LarryBattle Robię prawie to samo, ale myślę, że stosuje się do tablicy i c w wywołaniu przecinka (to znaczy przecinkami oddzielone argumenty). - Samih
@LarryBattle miły mnemonik Larry, w końcu przypomniał sobie korelację z imionami - Tomo
ty zastosować raz na pracę (jeden argument), [telefon] połączenie ludzie wiele razy (kilka argumentów). Alternatywa: jest ich zbyt wiele Połączenie gier Duty. - Gras Double


Odpowiedzi:


Różnica polega na tym apply pozwala wywołać funkcję za pomocą arguments jako tablica; call wymaga jawnie wymienionych parametrów. Przydatny jest mnemonik "ZA dla zarray i do dla doomma. "

Zobacz dokumentację MDN na zastosować i połączenie.

Pseudo składnia:

theFunction.apply(valueForThis, arrayOfArgs)

theFunction.call(valueForThis, arg1, arg2, ...)

Istnieje również, od ES6, możliwość spread tablica do użycia z call funkcja, możesz zobaczyć zgodność tutaj.

Przykładowy kod:

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



Należy dodać, że argumenty muszą być tablicami numerycznymi ([]). Tablice asocjacyjne ({}) nie będą działać. - Kevin Schroeder
@KevinSchroeder: w języku javascript, [] nazywa się szyk, {} nazywa się obiekt. - Martijn
Często zapominałem, który z nich bierze tablicę i który oczekuje od ciebie listy argumentów. Techniką, o której pamiętałem, jest to, gdy zaczyna się pierwsza litera tej metody za następnie pobiera tablicę np za pply tablica - aziz punjani
@SAM Korzystanie połączenie zamiast normalnego wywołania funkcji ma sens tylko wtedy, gdy musisz zmienić wartość to dla wywołania funkcji. Przykład (który przekształca funkcję arguments-object na tablicę): Array.prototype.slice.call(arguments) lub [].slice.call(arguments). zastosować ma sens, jeśli masz argumenty w tablicy, na przykład w funkcji, która wywołuje inną funkcję z (prawie) tymi samymi parametrami. Zalecenie Użyj zwykłego wywołania funkcji funcname(arg1) jeśli to robi, czego potrzebujesz, i oszczędzaj połączenie i zastosować na te specjalne okazje, kiedy naprawdę ich potrzebujesz. - some
@KunalSingh Both call i apply bierze dwa parametry. Pierwszy argument apply' and funkcja call musi być obiektem właściciela, a drugi parametr będzie odpowiednio parametrem tablicowym lub przecinkami. Jeśli zdasz null lub undefined jako pierwszy argument, a następnie w trybie nie ścisłym, są one zastępowane przez obiekt globalny tj. window - A J Qarshi


K. Scott Allen ma ładny zapis na temat.

Zasadniczo różnią się one sposobem obsługi argumentów funkcji.

Metoda apply () jest identyczna z call (), z wyjątkiem apply () wymaga tablicy jako drugiego parametru. Tablica reprezentuje argumenty dla metody docelowej. "

Więc:

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

209
2017-12-31 19:59



drugi parametr apply () i call () jest opcjonalny, nie jest wymagany. - angry kiwi
Pierwszy parametr również nie jest wymagany. - Ikrom


Aby odpowiedzieć na część dotyczącą tego, kiedy należy korzystać z każdej funkcji, użyj apply jeśli nie znasz liczby argumentów, które będziesz przekazywał, lub jeśli są już w tablicy lub obiekcie tablicowym (np. arguments obiekt do przekazywania własnych argumentów. Posługiwać się call w przeciwnym razie, ponieważ nie ma potrzeby zawijania argumentów w tablicy.

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

Kiedy nie przekazuję żadnych argumentów (jak na przykład), wolę call odkąd jestem powołanie funkcja. apply oznaczałoby, że jesteś stosowanie funkcja do (nieistniejących) argumentów.

Nie powinno być żadnych różnic w wydajności, może z wyjątkiem użycia apply i zawijaj argumenty w tablicy (np. f.apply(thisObject, [a, b, c]) zamiast f.call(thisObject, a, b, c)). Nie testowałem tego, więc mogą występować różnice, ale byłoby to bardzo specyficzne dla przeglądarki. Jest prawdopodobne, że call jest szybszy, jeśli nie masz jeszcze argumentów w tablicy i apply jest szybszy, jeśli to zrobisz.


150
2017-12-31 21:50





Oto dobry mnemonik. ZAZastosowania używają ZArrays i ZAlways przyjmuje jeden lub dwa argumenty. Kiedy używasz dowszystko, co musisz doLiczba argumentów.


102
2017-09-04 13:36



Przydatny mnemonik tam !. Zmienię "jeden lub dwa argumenty", aby powiedzieć "maksymalnie dwa argumenty", ponieważ ani pierwszy, ani drugi parametr apply jest wymagane. Nie jestem pewien, dlaczego ktoś zadzwoni apply lub call bez parametru. Wygląda na to, że ktoś próbuje się dowiedzieć, dlaczego tu jest stackoverflow.com/questions/15903782/... - dantheta
Więc kiedy użyć Call () i kiedy użyć Apply () .... - Krish


Chociaż jest to stary temat, chciałem tylko wskazać, że .call jest nieco szybszy niż .apply. Nie mogę powiedzieć dokładnie dlaczego.

Zobacz jsPerf, http://jsperf.com/test-call-vs-apply/3


[UPDATE!]

Douglas Crockford wspomina krótko różnicę między tymi dwoma, co może pomóc wyjaśnić różnicę w wydajności ... http://youtu.be/ya4UHuXNygM?t=15m52s

Zastosuj przyjmuje szereg argumentów, natomiast Call przyjmuje zero lub więcej indywidualnych parametrów! Ah hah!

.apply(this, [...])

.call(this, param1, param2, param3, param4...)


91
2017-11-07 17:36



To zależy od tego, co funkcja ma z parametrami / tablicą, jeśli nie musi przetwarzać tablicy, czy zajmuje to mniej czasu? - Relic
Co ciekawe, nawet bez tablicy, połączenie jest znacznie szybsze. jsperf.com/applyvscallvsfn2 - Josh Mc
@JoshMc To byłoby bardzo specyficzne dla przeglądarki. W IE 11, dostaję zastosowanie idąc dwa razy szybciej niż połączenie. - Vincent McNabb
1. Utworzenie nowej tablicy oznacza, że ​​garbage collector będzie musiał ją wyczyścić w pewnym momencie. 2. Uzyskiwanie dostępu do elementów w tablicy przy użyciu dereferencji jest mniej wydajne niż uzyskiwanie bezpośredniego dostępu do zmiennej (parametru). (Wierzę, że to jest to, co kmateny oznacza "parsowanie", co jest w rzeczywistości czymś zupełnie innym.) Ale żaden z moich argumentów nie wyjaśnia jsperf. To musi być związane z implementacją dwóch funkcji silnika, np. może i tak utworzą pustą tablicę, jeśli żadna nie zostanie przekazana. - joeytwiddle
Dziękujemy za udostępnienie testu i wideo - Gary


Podaje wyciąg z Zamknięcie: The Definitive Guide autorstwa Michaela Bolina. To może wyglądać na trochę za długie, ale jest nasycone wieloma wglądami. Z "Dodatku B. Często błędnie rozumiane pojęcia JavaScript":


Co this Odnosi się do Kiedy wywoływana jest funkcja

Podczas wywoływania funkcji formularza foo.bar.baz(), obiekt foo.bar jest określany jako odbiornik. Gdy funkcja jest wywoływana, to odbiornik jest używany jako wartość 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);

Jeśli nie ma wyraźnego odbiornika podczas wywoływania funkcji, wówczas obiekt globalny staje się odbiorcą. Jak wyjaśniono w "goog.global" na stronie 47, okno jest obiektem globalnym, gdy JavaScript jest wykonywany w przeglądarce internetowej. Prowadzi to do zaskakującego zachowania:

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

Nawet jeśli obj.addValues i f odnoszą się do tej samej funkcji, zachowują się inaczej po wywołaniu, ponieważ wartość odbiornika jest różna w każdym wywołaniu. Z tego powodu, wywołując funkcję, która odnosi się do this, ważne jest, aby to zapewnić this będzie mieć poprawną wartość, gdy zostanie wywołana. Aby było jasne, jeśli this nie zostały odwołane w treści funkcji, a następnie zachowanie f(20) i obj.addValues(20) byłby taki sam.

Ponieważ funkcje są obiektami najwyższej klasy w JavaScript, mogą mieć własne metody. Wszystkie funkcje mają metody call() i apply() które umożliwiają ponowne zdefiniowanie odbiornika (tj. obiektu, który this odnosi się do) podczas wywoływania funkcji. Sygnatury metody są następujące:

/**
* @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;

Zauważ, że jedyna różnica między call() i apply() czy to call() odbiera parametry funkcji jako indywidualne argumenty, natomiast apply() odbiera je jako pojedynczą tablicę:

// 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]);

Następujące połączenia są równoważne, jak f i obj.addValues odnoszą się do tej samej funkcji:

obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);

Ponieważ jednak nie call() ani apply() używa wartości własnego odbiornika do zastąpienia argumentu odbiornika, gdy jest on nieokreślony, następujące czynności nie będą działać:

// Both statements evaluate to NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);

Wartość this nigdy nie może być null lub undefined po wywołaniu funkcji. Gdy null lub undefined jest dostarczany jako odbiornik call() lub apply(), obiekt globalny jest używany jako wartość dla odbiornika. Dlatego poprzedni kod ma taki sam niepożądany efekt uboczny, jak dodanie właściwości o nazwie value do obiektu globalnego.

Pomocne może być myślenie, że funkcja nie ma wiedzy o zmiennej, do której jest przypisana. Pomaga to wzmocnić pogląd, że wartość tego będzie związana, gdy funkcja zostanie wywołana, a nie wtedy, gdy zostanie zdefiniowana.


Koniec ekstraktu.


71
2017-12-04 12:41



Po prostu, aby zwrócić uwagę na fakt, że additionalValuesnie ma odniesienia w środku obj.addValues ciało - Viktor Stolbin
Wiem, że odpowiadałeś na pytanie, ale chciałbyś dodać: mógłbyś użyć binda podczas definiowania f. var f = obj.addValues; staje się var f = obj.addValues.bind(obj)   a teraz f (20) działałby bez konieczności używania połączenia lub stosowania za każdym razem. - jhliberty


Przydaje się czasami, aby jeden obiekt pożyczał funkcję innego obiektu, co oznacza, że ​​pożyczający przedmiot po prostu wykonuje pożyczoną funkcję tak, jakby była własną.

Przykład małego kodu:

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

Te metody są bardzo przydatne do nadawania obiektom tymczasowej funkcjonalności.


33
2018-02-25 19:31



Dla osób, które chcą wiedzieć, jak zobaczyć console.log sprawdzić: Co to jest console.log i jak z niego korzystać? - Michel Ayres