2016-01-21 9 views
51

Ich habe eine JavaScript-Klasse, und jede Methode gibt eine Q Versprechen. Ich möchte wissen, warum this in method2 und method3 nicht definiert ist. Gibt es eine korrektere Möglichkeit, diesen Code zu schreiben?Warum ist 'this' bei der Verwendung von Versprechungen innerhalb der Klassenmethode undefiniert?

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; 
}; 

Ich kann dieses Problem beheben, indem bind mit:

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

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

aber nicht ganz sicher, warum bind notwendig ist; ist .then() töten this aus?

+0

Wenn Sie bind() verwenden, erstellt es eine andere Funktion mit genau dem Umfang, den Sie an Parametern übergeben werden. Obwohl es nur Ihre letzte Frage beantwortet, werfen Sie einen Blick auf die Dokumentation von Mozila: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Function/bind –

+0

In 8 Worten, erklären Sie, wie zum Teufel ist das (http://stackoverflow.com/questions/34930771/why-is-this-undefined-inside-class-method-when-using-promises) ein Duplikat von [das] (http: // stackoverflow.com/questions/591269/settimeout-and-this-in-javascript)? Ich hatte gerade genau diese Frage kommen, die _not_ von [das] (http://stackoverflow.com/questions/591269/settimeout-and-this-in-javascript) beantwortet worden wäre. Ich kenne [das] (http://stackoverflow.com/questions/591269/settimeout-and-this-in-javascript) bereits, aber ich komme mit Versprechen, ES6-Klassen und 'this' in Einklang. – toszter

+0

Obwohl es eng verwandt ist, ist dies kein Duplikat dieser Frage: http://stackoverflow.com/questions/591269/settimeout-and-this-in-javascript Oder ist "Warum fällt ein Apfel von einem Baum?" ein Duplikat der Frage "Warum bricht ein Kartenhaus zusammen, wenn ich den Tisch kippe, auf dem es gebaut ist?"? – lex82

Antwort

71

this ist immer das Objekt, an dem die Methode aufgerufen wird. Wenn Sie die Methode jedoch an then() übergeben, rufen Sie sie nicht an! Die Methode wird irgendwo gespeichert und von dort später aufgerufen. Wenn Sie this erhalten wollen, werden Sie es wie folgt tun:

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

oder wenn Sie es die Pre-ES6 Art und Weise zu tun haben, müssen Sie this bewahren vor:

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

große Antwort - oder pre-ES6 ".then (this.method2.bind (this))" –

+0

Ich benutzte '.then (data => this.method (data)) ' – Albert

0

Eine Möglichkeit, Funktionen erhalten ihren Kontext (this) von dem Objekt, an dem sie aufgerufen werden (weshalb method1 den richtigen Kontext hat - es auf this aufgerufen wird). Sie übergeben einen Verweis auf die Funktion selbst an then. Sie können sich vorstellen, dass die Umsetzung von then etwa wie folgt aussieht:

function then(callback) { 

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

In diesem Beispiel callback ist ein Verweis auf Ihre Funktion. Es hat keinen Kontext. Wie Sie bereits bemerkt haben, können Sie das umgehen, indem Sie die Funktion an einen Kontext binden, bevor Sie ihn an diesen übergeben.

15

Promise-Handler werden standardmäßig im Kontext des globalen Objekts (window) aufgerufen. Im strikten Modus (use strict;) lautet der Kontext undefined. Dies passiert mit method2 und method3.

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

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

Für method1, du bist method1 als this.method1() aufrufen. Diese Art des Aufrufs ruft sie im Kontext des Objekts this auf, das Ihre Instanz ist. Deshalb ist der Kontext in method1 die Instanz.

+0

Ich wurde verrückt !!!! bis ich deine Antwort sah, vielen Dank! –

2

Im Grunde übergeben Sie eine Funktionsreferenz ohne Kontextbezug. Der this Kontext wird auf verschiedene Arten bestimmt:

  • Implizit. Das Aufrufen einer globalen Funktion oder einer Funktion ohne Bindung setzt einen globalen Kontext voraus. *
  • Durch direkte Bezugnahme. Wenn Sie myObj.f() anrufen, dann wird myObj der this Kontext sein. **
  • Manuelle Bindung. Dies ist Ihre Klasse von Funktionen wie .bind und .apply. Diese geben Sie explizit an, was der this Kontext ist. Diese haben immer Vorrang vor den beiden vorherigen.
  • In Ihrem Beispiel übergeben Sie eine Funktionsreferenz, so dass sie beim Aufruf als globale Funktion oder ohne Kontext verwendet wird. Die Verwendung von .bind löst dies durch Erstellen einer neuen Funktion, wobei this explizit festgelegt ist.

    * Dies trifft nur im nicht strikten Modus zu. Im strikten Modus ist this auf undefined eingestellt.

    ** Angenommen, die von Ihnen verwendete Funktion wurde nicht manuell gebunden.