wake-up-neo.com

Warum ist 'this' innerhalb der Klassenmethode undefiniert, wenn Versprechen verwendet werden?

Ich habe eine Javascript-Klasse und jede Methode gibt ein Q-Versprechen zurück. Ich möchte wissen, warum this in method2 und method3 undefiniert ist. Gibt es einen richtigeren Weg, diesen Code zu schreiben?

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 dies mit bind beheben:

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()this ausgeschaltet?

73
SteamDev

this ist immer das Objekt, für das die Methode aufgerufen wird. Wenn Sie die Methode an then() übergeben, rufen Sie sie jedoch nicht auf! Die Methode wird irgendwo gespeichert und später aufgerufen. Wenn Sie this beibehalten möchten, müssen Sie dies folgendermaßen tun:

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

oder wenn Sie es vor ES6 tun müssen, müssen Sie this beibehalten,

var that = this;
// ...
.then(function() { that.method2() })
107
lex82

Promise-Handler werden standardmäßig im Kontext des globalen Objekts (window) aufgerufen. Im strikten Modus (use strict;) ist 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 rufen Sie method1 als this.method1() auf. Diese Art des Aufrufs ruft es im Kontext des this-Objekts auf, das Ihre Instanz ist. Deshalb ist der Kontext in method1 die Instanz.

18
Joseph

Grundsätzlich übergeben Sie eine Funktionsreferenz ohne Kontextreferenz. Der this-Kontext wird auf verschiedene Weise bestimmt:

  1. Implizit. Das Aufrufen einer globalen Funktion oder einer Funktion ohne Bindung setzt einen globalen Kontext voraus. *
  2. Durch direkte Bezugnahme. Wenn Sie myObj.f() aufrufen, wird myObj der this-Kontext sein. **
  3. 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 vorhergehenden.

In Ihrem Beispiel übergeben Sie eine Funktionsreferenz, dh sie wird beim Aufruf als globale Funktion oder als Funktion ohne Kontext bezeichnet. Die Verwendung von .bind löst dieses Problem auf, indem eine neue Funktion erstellt wird, in der this explizit festgelegt ist.

* Dies gilt nur für den nicht strengen Modus. Im strikten Modus ist this auf undefined gesetzt.

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

2
Mike Cluck

Eine Möglichkeit, wie Funktionen ihren Kontext (this) erhalten, ist das Objekt, für das sie aufgerufen werden (weshalb method1 den richtigen Kontext hat - er wird für this aufgerufen. Sie übergeben einen Verweis auf die Funktion selbst an then. Sie können sich vorstellen, dass die Implementierung von then ungefähr so ​​aussieht:

function then( callback ) {

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

In diesem Beispiel ist callback eine Referenz auf Ihre Funktion. Es hat keinen Kontext. Wie Sie bereits bemerkt haben, können Sie dies umgehen, indem Sie die Funktion an einen Kontext binden, bevor Sie sie dann übergeben.

0
James Allardice