Jaka jest różnica między call a apply?

Jaka jest różnica pomiędzy używaniem call i apply do wywołania funkcji?

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

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

Czy istnieją różnice wydajności między tymi dwoma wyżej wymienionymi metodami? Kiedy najlepiej używać call nad apply i odwrotnie?

Author: iridescent, 2009-12-31

20 answers

Różnica polega na tym, że apply pozwala na wywołanie funkcji z {[2] } jako tablicą; call wymaga jawnej listy parametrów. Przydatnym mnemonikiem jest "a dla a rray i C dla C omma."

Zobacz dokumentację MDN naapply icall .

Pseudo składnia:

theFunction.apply(valueForThis, arrayOfArgs)

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

Istnieje również, począwszy od ES6, możliwość spread tablica do użytku 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
 3347
Author: flatline,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-19 16:10:20

K. Scott Allen ma ładny writeup w tej sprawie.

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

Metoda apply () jest identyczna z metodą call (), z tym że apply () wymaga tablicy jako drugiego parametru. Tablica przedstawia argumenty metody docelowej."

Więc:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
 211
Author: notnoop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-19 20:53:50

Aby odpowiedzieć na pytanie, Kiedy używać każdej funkcji, Użyj apply, jeśli nie znasz liczby argumentów, które będziesz przesyłać, lub jeśli są one już w tablicy lub obiekcie podobnym do tablicy (jak obiekt arguments, aby przesłać własne argumenty. Użyj call w przeciwnym razie, ponieważ nie ma potrzeby zawijania argumentów w tablicę.

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 w twoim przykładzie), wolę call ponieważ jestem wywołaniem funkcji. apply sugerowałoby, że jesteś zastosowanie funkcji do (nieistniejących) argumentów.

Nie powinno być żadnych różnic w wydajności, chyba że użyjesz apply i zawiniesz argumenty w tablicę (np. f.apply(thisObject, [a, b, c]) zamiast f.call(thisObject, a, b, c)). Nie testowałem tego, więc mogą być różnice, ale byłoby to bardzo specyficzne dla przeglądarki. Jest prawdopodobne, że call jest szybsze, jeśli nie masz jeszcze argumentów w tablicy, a {[1] } jest szybsze, jeśli to zrobisz.

 151
Author: Matthew Crumley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-31 21:56:01

Oto dobry mnemonic. Apply używa Arrays i Always pobiera jeden lub dwa argumenty. Kiedy używasz Cwszystko, co musisz C ount liczby argumentów.

 102
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-04 18:21:25

Chociaż jest to stary temat, chciałem tylko zwrócić na to uwagę .połączenie jest nieco szybsze niż .aplikuj. Nie mogę dokładnie powiedzieć dlaczego.

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


[UPDATE!]

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

Apply pobiera tablicę argumentów, podczas gdy Call pobiera zero lub więcej pojedynczych parametry! Ah hah!

.apply(this, [...])

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

 91
Author: kmatheny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-18 17:44:50

Wynika z wyciągu zClosure: the Definitive Guide Michaela Bolina . To może wyglądać trochę długi, ale jest nasycony dużo wglądu. Z "Dodatku B. Często Niezrozumiane Pojęcia JavaScript":


Co this odnosi się do funkcji wywołanej

Podczas wywoływania funkcji w postaci foo.bar.baz(), obiekt {[8] } jest określany jako odbiornik. Gdy funkcja jest wywoływana, to odbiornik jest używany jako wartość dla 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 jawnego odbiornika podczas wywoływania funkcji, to obiekt globalny staje się odbiornikiem. Jak wyjaśniono w "goog.global " na stronie 47 okno jest obiektem globalnym, gdy JavaScript jest wykonywany w przeglądarce internetowej. To prowadzi 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

Mimo że obj.addValues i f odnoszą się do tej samej funkcji, zachowują się inaczej, gdy są wywoływane, ponieważ wartość odbiornika jest inna w każdym wywołaniu. Z tego powodu, podczas wywoływania funkcja, która odnosi się do this, Ważne jest, aby upewnić się, że this będzie miała prawidłową wartość, gdy zostanie wywołana. Aby było jasne, gdyby this nie były odwołane w ciele funkcji, to zachowanie f(20) i obj.addValues(20) byłoby takie samo.

Ponieważ funkcje są obiektami pierwszej klasy w JavaScript, mogą mieć własne metody. Wszystkie funkcje posiadają metody call() i apply(), które umożliwiają ponowne zdefiniowanie odbiornika (tj. obiektu, do którego odnosi się this) podczas wywoływania funkcja. Podpisy 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 jedyną różnicą pomiędzy call() i apply() jest to, że call() otrzymuje parametry funkcji jako indywidualne argumenty, podczas gdy apply() otrzymuje 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]);

Poniższe wywołania są równoważne, ponieważ 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ż ani call(), ani apply() nie używają wartości własnego odbiornika do zastąpienia argumentu odbiornika, gdy jest nieokreślone, następujące 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, gdy wywołana jest funkcja. Gdy null lub undefined jest dostarczany jako odbiornik do call() lub apply(), obiekt globalny jest używany jako wartość dla odbiornika. Dlatego też poprzedni kod ma ten sam niepożądany efekt uboczny dodania właściwości o nazwie value do obiektu globalnego.

Pomocne może być myślenie o funkcji jako o braku wiedzy o zmiennej, do której jest przypisany. Pomaga to wzmocnić ideę, że wartość tego będzie związana, gdy funkcja zostanie wywołana, a nie gdy zostanie zdefiniowana.


Koniec wyciągu.

 72
Author: Dominykas Mostauskis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-24 16:15:33

Jest to przydatne czasami dla jednego obiektu zapożyczyć funkcję innego obiektu, co oznacza, że pożyczający obiekt po prostu wykonuje pożyczoną funkcję, 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
Author: tjacks3,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-25 19:31:51

Kolejny przykład z Call, Apply i Bind. Różnica między Call a Apply jest oczywista, ale Bind działa tak:

  1. Bind zwraca instancję funkcji, którą można wykonać
  2. pierwszy parametr to " this "
  3. drugi parametr to oddzielona przecinkami lista argumentów (jak wywołanie)

}

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
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-25 18:50:04

Chciałbym pokazać przykład, gdzie używany jest argument '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] 

**Szczegóły: http://es5.github.io/#x15.4.4.7 *

 21
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-16 12:47:13

Call() pobiera argumenty rozdzielone przecinkami, np:

.call(scope, arg1, arg2, arg3)

I apply() pobiera tablicę argumentów, np:

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

Oto kilka przykładów użycia: http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/

 20
Author: Mark Karwowski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-25 16:40:32

Od funkcja MDN docs on.prototyp.zastosuj() :

Metoda apply() wywołuje funkcję o podanej wartości this i argumenty podane jako tablica (lub obiekt podobny do tablicy).

Składnia

fun.apply(thisArg, [argsArray])

Od funkcja MDN docs on.prototyp.call() :

Metoda call() wywołuje funkcję z podaną wartością this i argumentami podanymi indywidualnie.

Składnia

fun.call(thisArg[, arg1[, arg2[, ...]]])

Od Funkcja.Zastosuj i funkcjonuj.wywołanie w JavaScript :

Metoda apply() jest identyczna jak call (), z tym że apply () wymaga tablica jako drugi parametr. Tablica przedstawia argumenty dla metoda celu.


Przykład kodu:

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'
));

Zobacz this Fiddle.

 18
Author: John Slegers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-20 17:06:56

Zasadnicza różnica polega na tym, że call() przyjmuje lista argumentów, while apply() akceptuje pojedyncza tablica argumentów.

 11
Author: Rakesh Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-28 04:57:48

Oto mały post, napisałem na ten temat:

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
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-04 13:58:29

Różnica polega na tym, że call() pobiera argumenty funkcji oddzielnie, a apply() pobiera argumenty funkcji w tablicy.

 7
Author: Sanjib Debnath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-28 06:41:37

Możemy odróżnić metody call I apply jak poniżej

CALL: funkcja z argumentem provide indywidualnie. Jeśli znasz argumenty do przekazania lub nie ma argumentów do przekazania, możesz użyć call.

APPLY: wywołanie funkcji z argumentem podanym jako tablica. Możesz użyć apply, jeśli nie wiesz, ile argumentów zostanie przekazanych do funkcji.

Jest zaleta użycia apply over call, nie musimy zmieniać liczby argumentów tylko możemy zmienić tablica, która jest przekazywana.

Nie ma dużej różnicy w wydajności. Ale możemy powiedzieć, że wywołanie jest nieco szybsze niż compare to apply, ponieważ tablica musi być oceniana w metodzie apply.

 6
Author: Praveen D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-06 11:50:46

Różnica między tymi metodami polega na tym, jak chcesz przekazać parametry.

"a dla tablicy i C dla przecinka" jest przydatnym mnemonicznym.

 5
Author: venkat7668,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-03 06:15:16

Call I apply są używane do wymuszania wartości this podczas wykonywania funkcji. Jedyną różnicą jest to, że call przyjmuje n+1 argumenty, gdzie 1 jest this i 'n' arguments. apply pobiera tylko dwa argumenty, jeden to this drugi to tablica argumentów.

Zaletą, którą widzę w apply nad call jest to, że możemy łatwo delegować wywołanie funkcji do innej funkcji bez większego wysiłku;

function sayHello() {
  console.log(this, arguments);
}

function hello() {
  sayHello.apply(this, arguments);
}

var obj = {name: 'my name'}
hello.call(obj, 'some', 'arguments');

Zauważ jak łatwo delegowaliśmy hello do sayHello używając apply, ale z call jest to bardzo trudne do osiągnięcia.

 5
Author: Raghavendra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-24 08:36:02

Mimo, że call i apply działają w ten sam sposób, myślę, że jest przynajmniej jedno miejsce, w którym nie można używać call, ale można używać tylko apply. To jest, gdy chcesz wspierać dziedziczenie i chcesz zadzwonić do konstruktora.

Tutaj jest funkcja pozwalająca na tworzenie klas, która wspiera również tworzenie klas poprzez rozszerzanie innych klas.

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
Author: Dhana Krishnasamy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-12 18:10:05

Główna różnica polega na tym, że za pomocą wywołania możemy zmienić zakres i przekazać argumenty jak zwykle, ale apply pozwala wywołać je używając argumentów jako tablicy(przekazać je jako tablicę). Ale pod względem tego, co mają robić w Twoim kodzie, są bardzo podobne.

Podczas gdy składnia tej funkcji jest niemal identyczna z apply (), zasadnicza różnica polega na tym, że call() przyjmuje argument list, podczas gdy apply () akceptuje pojedynczą tablicę argumentów.

Więc jak widzisz, tam nie jest to duża różnica, ale nadal są przypadki, w których preferujemy użycie funkcji call () lub apply (). Na przykład, spójrz na poniższy kod, który wyszukuje najmniejszą i największą liczbę w tablicy z MDN, używając metody Zastosuj:

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

Więc główną różnicą jest tylko sposób, w jaki przekazujemy argumenty:

Call:

function.call(thisArg, arg1, arg2, ...);

Zastosuj:

function.apply(thisArg, [argsArray]);
 3
Author: Alireza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-23 01:40:25

Podsumowanie:

Zarówno call() jak i apply() są metodami znajdującymi się na Function.prototype. Dlatego są one dostępne dla każdego obiektu funkcyjnego poprzez łańcuch prototypów. Zarówno call(), jak i apply() mogą wykonywać funkcję o określonej wartości this.

Główną różnicą między call() i apply() jest sposób, w jaki musisz przekazać w nim argumenty. W obu call() i apply() przekazujesz jako pierwszy argument obiekt, który chcesz mieć jako wartość this. Pozostałe argumenty różnią się w następujący sposób:

  • Z call() musisz wstawić argumenty normalnie (zaczynając od drugiego argumentu)
  • Z apply() Musisz podać tablicę argumentów.

Przykład:

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

Dlaczego miałbym używać tych funkcji?

Wartość this może być czasami trudna w javascript. Wartość this określona gdy funkcja jest wykonywana, a nie gdy funkcja jest zdefiniowana. Jeśli nasza funkcja jest zależna po prawej this Wiązanie możemy użyć call() i apply() aby wyegzekwować to zachowanie. Na przykład:

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
 1
Author: Willem van der Veen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-29 15:29:42