javascript bind




Qual è la differenza tra call e apply? (14)

Sommario:

Sia call() che apply() sono metodi che si trovano su Function.prototype . Pertanto sono disponibili su ogni oggetto funzione tramite la catena del prototipo. call() e apply() possono eseguire una funzione con un valore specificato di this .

La principale differenza tra call() e apply() è il modo in cui devi passare argomenti in esso. Sia in call() che apply() si passa come primo argomento l'oggetto che si desidera sia il valore come this . Gli altri argomenti differiscono nel modo seguente:

  • Con call() devi inserire gli argomenti normalmente (partendo dal secondo argomento)
  • Con apply() devi passare in array di argomenti.

Esempio:

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

Perché dovrei usare queste funzioni?

this valore può essere difficile a volte in javascript. Il valore di this determinato quando viene eseguita una funzione non quando viene definita una funzione. Se la nostra funzione dipende da un diritto di this associazione, possiamo usare call() e apply() per applicare questo comportamento. Per esempio:

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

Qual è la differenza tra l'uso di una call e l' apply per invocare una funzione?

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

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

Ci sono differenze di prestazioni tra i due metodi sopra menzionati? Quando è meglio usare call over apply e viceversa?


A volte è utile che un oggetto prenda in prestito la funzione di un altro oggetto, il che significa che l'oggetto in prestito esegue semplicemente la funzione prestata come se fosse propria.

Un piccolo esempio di codice:

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

Questi metodi sono molto utili per dare agli oggetti funzionalità temporanee.



Chiama e applica entrambi vengono utilizzati per forzare this valore quando viene eseguita una funzione. L'unica differenza è che la call accetta n+1 argomenti dove 1 è this e 'n' arguments . apply richiede solo due argomenti, uno è this l'altro è l'array di argomenti.

Il vantaggio che vedo apply sulla call è che possiamo facilmente delegare una chiamata di funzione ad un'altra funzione senza molto sforzo;

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

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

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

Osserva con quanta facilità abbiamo delegato hello a sayHello hello usando apply , ma con call è molto difficile da raggiungere.


Ecco un buon mnemonico. A pply usa A rrays e A lways richiede uno o due argomenti. Quando usi C tutto ciò che devi è calcolare il numero di argomenti.


Ecco un post piccolo, ho scritto su questo:

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"

La differenza è che apply ti consente di invocare la funzione con arguments come una matrice; call richiede che i parametri siano elencati esplicitamente. Un utile mnemonico è " A for a rray and C for c omma".

Vedi la documentazione di MDN su apply e call .

Pseudo sintassi:

theFunction.apply(valueForThis, arrayOfArgs)

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

C'è anche, a partire da ES6, la possibilità di spread la matrice per l'uso con la funzione di call , è possibile vedere here le compatibilità.

Codice di esempio:

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


La differenza è che call() prende gli argomenti della funzione separatamente, e apply() prende gli argomenti della funzione in una matrice.


La differenza principale è che, utilizzando la chiamata, è possibile modificare l'ambito e passare gli argomenti normalmente, ma applica consente di chiamarlo utilizzando argomenti come matrice (passarli come matrice). Ma in termini di cosa devono fare nel tuo codice, sono piuttosto simili.

Mentre la sintassi di questa funzione è quasi identica a quella di apply (), la differenza fondamentale è che call () accetta un elenco di argomenti, mentre apply () accetta un singolo array di argomenti.

Quindi, come vedi, non c'è una grande differenza, ma ci sono ancora casi in cui preferiamo usare call () o apply (). Ad esempio, guarda il codice qui sotto, che trova il numero più piccolo e più grande in un array da MDN, usando il metodo 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)

Quindi la differenza principale è il modo in cui passiamo gli argomenti:

Chiamata:

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

Applicare:

function.apply(thisArg, [argsArray]);

La differenza tra questi metodi sono, come si desidera passare i parametri.

"A per array e C per comma" è un utile mnemonico.


Mi piacerebbe mostrare un esempio, in cui viene utilizzato l'argomento '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] 

** dettagli: http://es5.github.io/#x15.4.4.7 *


Per rispondere alla parte su quando utilizzare ciascuna funzione, utilizzare apply se non si conosce il numero di argomenti che si passeranno, o se sono già in un array o in un oggetto simile a un array (come l'oggetto arguments per inoltrare il proprio argomenti: usa call altrimenti, poiché non è necessario racchiudere gli argomenti in un array.

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

Quando non sto passando alcun argomento (come nel tuo esempio), preferisco call dal momento che sto chiamando la funzione. apply implicherebbe che stai applicando la funzione agli argomenti (inesistenti).

Non ci dovrebbero essere differenze di prestazioni, tranne forse se si usa apply e wrap gli argomenti in un array (ad esempio f.apply(thisObject, [a, b, c]) invece di f.call(thisObject, a, b, c) ). Non l'ho testato, quindi potrebbero esserci differenze, ma sarebbe molto specifico per il browser. È probabile che la call sia più veloce se non si hanno già gli argomenti in una matrice e se si apply è più veloce.


Segue un estratto da Closure: The Definitive Guide di Michael Bolin . Potrebbe sembrare un po 'lungo, ma è saturo di molte intuizioni. Da "Appendice B. Concetti JavaScript frequentemente fraintesi":

Che cosa this riferisce a quando viene chiamata una funzione

Quando si chiama una funzione del modulo foo.bar.baz() , l'oggetto foo.bar viene chiamato ricevitore. Quando viene chiamata la funzione, è il ricevitore che viene utilizzato come valore per 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);

Se non è presente un destinatario esplicito quando viene chiamata una funzione, l'oggetto globale diventa il destinatario. Come spiegato in "goog.global" a pagina 47, window è l'oggetto globale quando JavaScript viene eseguito in un browser web. Ciò porta ad alcuni comportamenti sorprendenti:

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

Anche se obj.addValues f riferiscono alla stessa funzione, si comportano diversamente quando vengono chiamati perché il valore del ricevitore è diverso in ogni chiamata. Per questo motivo, quando si chiama una funzione che fa riferimento a this , è importante assicurarsi che this abbia il valore corretto quando viene chiamato. Per essere chiari, se this non fosse referenziato nel corpo della funzione, allora il comportamento di f(20) e obj.addValues(20) sarebbe lo stesso.

Poiché le funzioni sono oggetti di prima classe in JavaScript, possono avere i propri metodi. Tutte le funzioni hanno i metodi call() e apply() che rendono possibile ridefinire il destinatario (cioè l'oggetto a cui this riferisce) quando chiama la funzione. Le firme del metodo sono le seguenti:

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

Si noti che l'unica differenza tra call() e apply() è che call() riceve i parametri della funzione come singoli argomenti, mentre apply() li riceve come un singolo array:

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

Le seguenti chiamate sono equivalenti, poiché f e obj.addValues riferiscono alla stessa funzione:

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

Tuttavia, poiché né call()apply() utilizza il valore del proprio ricevitore per sostituire l'argomento ricevitore quando non è specificato, il seguente non funzionerà:

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

Il valore di this non può mai essere null o undefined quando viene chiamata una funzione. Quando null o undefined viene fornito come ricevitore da call() o apply() , l'oggetto globale viene utilizzato come valore per il ricevitore. Pertanto, il codice precedente ha lo stesso effetto collaterale indesiderato di aggiungere una proprietà denominata value all'oggetto globale.

Può essere utile pensare a una funzione come se non avesse conoscenza della variabile a cui è assegnata. Ciò aiuta a rafforzare l'idea che il valore di questo sarà associato quando viene chiamata la funzione piuttosto che quando è definita.

Fine dell'estratto


Un altro esempio con Call, Apply e Bind. La differenza tra Call e Apply è evidente, ma Bind funziona in questo modo:

  1. Bind restituisce un'istanza di una funzione che può essere eseguita
  2. Il primo parametro è ' questo '
  3. Il secondo parametro è un elenco di argomenti separati da virgola (come 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
*/






dynamic