Quando passi “questo” come argomento

Sto cercando di conoscere this , e mi sta confondendo un po ‘qui:

 var randomFunction = function(callback) { var data = 10; callback(data); }; var obj = { initialData: 20, sumData: function(data) { var sum = this.initialData + data; console.log(sum); }, prepareRandomFunction: function() { randomFunction(this.sumData.bind(this)); } }; obj.prepareRandomFunction(); 

È progettato per impostarsi dove viene prima visualizzato in codice? Per esempio, nel mio esempio lo sto usando con successo per riferirsi a obj e anche bind la funzione a obj , ma poiché this è passata come funzione di callback, cosa lo impedisce di essere impostata come funzione randomFunction (cioè cosa lo ferma da passando letteralmente “this.sumData.bind (this)” in modo che this sia impostato su randomFunction quando viene chiamato da lì)?

Sono un noob che cerca di imparare. Grazie.

Aggiornato Non sto esattamente chiedendo come funziona in generale (non credo). Sono principalmente curioso di sapere perché this viene impostato dove lo definisco come argomento della mia chiamata a randomFunction , e non dove callback viene chiamato all’interno di randomFunction . Potrei sbagliarmi, ma se dovessi scambiare this.sumData.bind(this) con il callback(data) che attualmente ho penso che this.sumData.bind(this) un risultato diverso. È perché il callback è un riferimento a this.sumData.bind(this) quando è stato definito per la prima volta (e dove this è obj )?


Penso di aver imparato attraverso questo scenario che this è impostato quando viene eseguito. Non viene passato come argomento per essere impostato in seguito quando l’argomento viene chiamato in linea.

this all’interno di una chiamata di funzione viene impostato in base a come viene chiamata una funzione. Ci sono sei modi principali in cui this viene impostato.

  1. Chiamata di funzione normale: in una normale chiamata di funzione come foo() , this è impostato sull’object globale (che è una window in un browser) o su undefined (in modalità rigorosa di Javascript).

  2. Chiamata metodo: se viene chiamato un metodo come obj.foo() , this viene impostato su obj all’interno della funzione.

  3. .apply () o .call (): se si utilizza .apply() o .call() , this viene impostato in base a ciò che viene passato a .apply() o .call() . Ad esempio, potresti fare foo.call(myObj) e far sì che this sia impostato su myObj all’interno di foo() per quella particolare chiamata di funzione.

  4. Usando new: se chiami una funzione con new come new foo() , allora viene creato un nuovo object e la funzione di costruzione foo viene chiamata con this set all’object appena creato.

  5. Uso di .bind (): quando si usa .bind() viene restituita una nuova funzione di stub da quella chiamata che usa internamente .apply() per impostare il puntatore come passato a .bind() . Per tua informazione, questo non è un caso diverso perché .bind() può essere implementato con .apply() .

  6. Uso di ES6 Fat Arrow Function La definizione di una funzione tramite la syntax della freccia in ES6 + legherà il valore lessicale corrente ad essa. Quindi, indipendentemente da come viene chiamata la funzione altrove, this valore verrà impostato dall’interprete sul valore che this ha quando è stata definita la funzione. Questo è completamente diverso da tutte le altre chiamate di funzione.

C’è una sorta di settimo metodo, tramite una funzione di callback , ma non è propriamente un suo schema, ma piuttosto la funzione che chiama il callback usa uno degli schemi sopra e determina quale sarà il valore di this quando viene richiamato il callback . È necessario consultare la documentazione o il codice per la funzione di chiamata o testarlo personalmente per determinare a cosa verrà impostato in una richiamata.


Ciò che è importante capire in Javascript è che ogni singola funzione o chiamata di metodo in Javascript imposta un nuovo valore per this . E, quale valore è impostato è determinato da come viene chiamata la funzione.

Quindi, se si passa un metodo come callback semplice, quel metodo non verrà chiamato come obj.method() e quindi non avrà il valore corretto di this set per esso. Puoi usare .bind() per ovviare a questo problema.

È anche utile sapere che alcune funzioni di callback (come i gestori di eventi DOM) vengono chiamate con un valore specifico come impostato dall’infrastruttura che chiama la funzione di callback. Internamente, tutti usano .call() o .apply() quindi questa non è una nuova regola, ma è qualcosa di cui essere a conoscenza. Il “contratto” per una funzione di callback può comprendere come imposta il valore di this . Se non imposta esplicitamente il valore di this , sarà impostato in base alla regola n.

In ES6, chiamando una funzione tramite una funzione freccia, si mantiene il valore lessicale corrente di this . Ecco un esempio della funzione array che mantiene il lessicale this da MDN :

 function Person(){ this.age = 0; setInterval(() => { this.age++; // |this| properly refers to the person object }, 1000); } var p = new Person(); 

Il tuo esempio di obj.prepareRandomFunction(); è la regola n. 2 sopra, quindi this sarà impostata su obj .

Il tuo esempio di randomFunction(this.sumData.bind(this)) è la regola n. 1 in alto, quindi this interna di randomFunction verrà impostata sull’object globale o undefined (se in modalità strict).

Dato che randomFunction chiama una funzione di callback che ha usato .bind() , allora il valore di this all’interno della funzione di callback quando viene chiamato verrà impostato sul valore di this che è stato passato a .bind() in this.sumData.bind(this) come da regola # 5 sopra. .bind() realtà crea una nuova funzione, il compito è chiamare la funzione originale DOPO aver impostato un valore personalizzato di this .


Qui ci sono un paio di altri riferimenti sull’argomento:

Come evitare “questo” riferendosi all’elemento DOM, e fare riferimento all’object

Una migliore comprensione di questo

Come funziona la parola chiave “this”?


Nota che con l’uso di .apply() o .call() o .bind() , puoi creare ogni sorta di cose un po ‘strane e cose a volte abbastanza utili che non potrebbero mai essere fatte in qualcosa come C ++. Puoi prendere qualsiasi funzione o metodo nel mondo e chiamarlo come se fosse un metodo di qualche altro object.

Ad esempio, questo è spesso usato per fare una copia degli oggetti nell’object arguments in un array:

 var args = Array.prototype.slice.call(arguments, 0); 

o allo stesso modo:

 var args = [].slice.call(arguments, 0); 

Questo prende il metodo .slice() dell’array e lo chiama, ma lo fornisce con un object arguments come this puntatore. L’object arguments (anche se non è un array effettivo) ha una funzionalità abbastanza simile a un array che il metodo .slice() può operare su di esso e finisce per fare una copia degli arguments in un array reale che può quindi essere utilizzato su direttamente con operazioni di array reali. Questo tipo di imbroglio non può essere fatto volenti o nolenti. Se il metodo array .slice() basa su altri metodi dell’array che non sono presenti sull’object arguments , questo trucco non funzionerebbe, ma dal momento che si basa solo su [] e .length , entrambi gli arguments hanno, funziona davvero.

Quindi, questo trucco può essere utilizzato per “prendere in prestito” i metodi da qualsiasi object e applicarli a un altro object a condizione che l’object che si sta applicando supporti qualsiasi metodo o proprietà che il metodo effettivamente utilizza. Questo non può essere fatto in C ++ perché i metodi e le proprietà sono “hard bound” in fase di compilazione (anche i metodi virtuali in C ++ sono associati a una specifica posizione v-table stabilita in fase di compilazione), ma possono essere facilmente eseguiti in Javascript perché proprietà e i metodi sono ricercati dal vivo in fase di esecuzione tramite il loro nome effettivo, quindi qualsiasi object che contenga le proprietà e i metodi corretti funzionerà con qualsiasi metodo che operi su quelli.

Passo dopo passo.

1) this all’interno di prepareRandomFunction è obj

 obj.prepareRandomFunction() 

2) randomFunction accetta una funzione:

 randomFunction(this.sumData); 

3) questa funzione viene chiamata:

 callback(data); 

avviso callback è chiamato senza un punto, il che significa che non ha alcun valore per this , il che significa che this è l’object globale (o undefined in modalità rigorosa).

4) sumData viene chiamato:

 var sum = this.initialData + data; 

this è l’object globale, initialData non esiste, si aggiunge undefined ai data . Risultati inaspettati

Soluzione: legalo in modo permanente:

 randomFunction(this.sumData.bind(this)); 

4) esegue sumData , this è obj , obj.initialData è 20 . Funziona.

È progettato per impostarsi dove viene prima visualizzato in codice?

No. È impostato su come viene chiamata una funzione o usando bind .

Ad esempio, nel mio esempio lo sto usando con successo per fare riferimento a obj

Perché la funzione è chiamata usando:

 obj.prepareRandomFunction(); 

che lo imposta nella funzione su obj .

e anche vincolare la funzione a obj

in:

 var obj = { ... prepareRandomFunction: function() { randomFunction(this.sumData.bind(this)); } }; 

poiché prepareRandomFunction è stato chiamato con obj come this , allora il valore di this all’interno di obj.sumData è impostato su obj e la chiamata a randomFunction si risolve in modo efficace a:

  randomFunction(obj.sumData.bind(obj)); 

ma dal momento che questo viene passato come una funzione di callback, cosa lo impedisce di essere impostato come funzione casuale

Il fatto che tu abbia impostato questo su obj nella chiamata. Se si desidera che questa funzione sia casuale , è necessario impostarla. randomFunction non ha valore intrinseco per questo , è impostato da come viene chiamata la funzione (o uso di bind ).

(cioè cosa lo impedisce di passare letteralmente “this.sumData.bind (this)” in modo che questo sia impostato su randomFunction)?

Perché questa non è una funzione casuale quando viene eseguito quel codice.

Così:

 obj.prepareRandomFunction(); 

chiama prepareRandomFunction con questo set su obj , che poi fa (sostituendo questo con obj :

 randomFunction(obj.sumData.bind(obj)); 

All’interno di randomFunction , questo non è stato impostato in modo tale che il valore predefinito sia l’object globale (irrilevante in quanto non viene utilizzato qui). Poi:

 var randomFunction = function(callback) { var data = 10; callback(data); }; 

Che crea un dato variabile locale con un valore di 10, quindi chiama sumData con il suo insieme su obj e passa il valore dei dati . Poi:

 sumData: function(data) { var sum = this.initialData + data; console.log(sum); }, 

e poiché questo è obj , l’assegnazione si risolve in modo efficace a:

  var sum = obj.initialData + 10; 

che è 30.

questo è un autoreferenziale di un object. Quando si crea un object, usando il nuovo operatore, dichiarandolo come un object: letterale o
chiamando uno dei built-in di JS, questo verrà assegnato ad esso.
Ma dato che lo scope di JS è dinamico e piuttosto non transitorio, di solito non si può fare troppo affidamento su questo – object. Questo è il motivo per cui spesso vedi costrutti come oThis: questo o var o