2016-01-21 28 views
51

Ho una classe javascript e ogni metodo restituisce una promessa Q. Voglio sapere perché this non è definito in method2 e method3. C'è un modo più corretto per scrivere questo codice?Perché "questo" non è definito nel metodo di classe quando si utilizzano le promesse?

function MyClass(opts){ 
    this.options = opts; 

    return this.method1() 
    .then(this.method2) 
    .then(this.method3); 
} 

MyClass.prototype.method1 = function(){ 
    // ...q stuff... 

    console.log(this.options); // logs "opts" object 

    return deferred.promise; 
}; 

MyClass.prototype.method2 = function(method1resolve){ 
    // ...q stuff... 

    console.log(this); // logs undefined 

    return deferred.promise; 
}; 

MyClass.prototype.method3 = function(method2resolve){ 
    // ...q stuff... 

    console.log(this); // logs undefined 

    return deferred.promise; 
}; 

posso risolvere questo problema utilizzando bind:

function MyClass(opts){ 
    this.options = opts; 

    return this.method1() 
    .then(this.method2.bind(this)) 
    .then(this.method3.bind(this)); 
} 

Ma non è del tutto sicuro perché bind è necessario; è .then() uccidendo this off?

+0

Quando si utilizza bind(), viene creata un'altra funzione con esattamente l'ambito che verrà passato dai parametri. Sebbene risponda solo alla tua ultima domanda, dai un'occhiata alla documentazione di Mozila: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Function/bind –

+0

In 8 parole, spiega come the heck is [this] (http://stackoverflow.com/questions/34930771/why-is-this-undefined-inside-class-method-when-using-promises) un duplicato di [that] (http: // stackoverflow.com/questions/591269/settimeout-and-this-in-javascript)? Ho appena avuto questa stessa identica domanda venire, che sarebbe _non_ sono stati risposto [che] (http://stackoverflow.com/questions/591269/settimeout-and-this-in-javascript). Conosco [che] (http://stackoverflow.com/questions/591269/settimeout-and-this-in-javascript) già, ma vengo a patti con le promesse, le classi ES6 e "questo". – toszter

+0

Sebbene sia strettamente correlato, questo non è un duplicato di questa domanda: http://stackoverflow.com/questions/591269/settimeout-and-this-in-javascript O è "Perché una mela cade da un albero?" un duplicato della domanda "Perché un castello di carte crolla quando inclino il tavolo su cui è costruito?"? – lex82

risposta

71

this è sempre l'oggetto su cui viene richiamato il metodo. Tuttavia, quando si passa il metodo a then(), non lo si sta chiamando! Il metodo verrà memorizzato da qualche parte e chiamato da lì in seguito. Se si desidera conservare this, si dovrà fare in questo modo:

.then(() => this.method2()) 

o se dovete farlo nel modo pre-ES6, è necessario conservare la this prima:

var that = this; 
// ... 
.then(function() { that.method2() }) 
+4

ottima risposta - o pre-ES6 ".questo (this.method2.bind (this))" –

+0

Ho usato '.then (data => this.method (data)) ' – Albert

0

funzione unidirezionale ottenere il loro contesto (this) è dall'oggetto su cui sono invocati (che è il motivo per cui method1 ha il giusto contesto - viene invocato su this). Stai passando un riferimento alla funzione stessa a then. Si può immaginare che l'attuazione di then simile a questa:

function then(callback) { 

    // assume 'value' is the recently-fulfilled promise value 
    callback(value); 
} 

In quell'esempio callback è un riferimento alla funzione. Non ha alcun contesto. Come hai già notato, puoi aggirare il problema legando la funzione a un contesto prima di passarlo a quel momento.

15

I gestori di promessa vengono chiamati nel contesto dell'oggetto globale (window) per impostazione predefinita. In modalità rigorosa (use strict;), il contesto è undefined. Questo è ciò che sta accadendo a method2 e method3.

;(function(){ 
    'use strict' 
    Promise.resolve('foo').then(function(){console.log(this)}); // undefined 
}()); 

;(function(){ 
    Promise.resolve('foo').then(function(){console.log(this)}); // window 
}()); 

Per method1, si sta chiamando method1 come this.method1(). Questo modo di chiamarlo lo chiama nel contesto dell'oggetto this che è la tua istanza. Ecco perché il contesto all'interno di method1 è l'istanza.

+0

Stavo impazzendo !!!! fino a quando ho visto la tua risposta, grazie mille! –

2

Fondamentalmente, si passa a un riferimento di funzione senza riferimento al contesto. Il contesto this è determinato in alcuni modi:

  1. In modo implicito. Chiamare una funzione globale o una funzione senza associazione presuppone un contesto globale. *
  2. Per riferimento diretto. Se si chiama myObj.f() allora myObj sarà il contesto this. **
  3. Associazione manuale. Questa è la tua classe di funzioni come .bind e .apply. Questi esplicitamente dichiarano che cos'è il contesto this. Questi hanno sempre la precedenza rispetto ai due precedenti.

Nel tuo esempio, stai passando un riferimento di funzione, quindi alla sua invocazione è implicita una funzione globale o uno senza contesto. L'utilizzo di .bind risolve questo creando una nuova funzione in cui this è impostato in modo esplicito.

* Questo è vero solo in modalità non rigida. In modalità rigorosa, this è impostato su undefined.

** Supponendo che la funzione che si sta utilizzando non è stato rilegato manualmente.