为什么在使用 promises 时 'this' 未在 class 方法中定义?

Why is 'this' undefined inside class method when using promises?

我有一个 javascript class,每个方法 returns 一个 Q 承诺。我想知道为什么 thismethod2method3 中未定义。这段代码有没有更正确的写法?

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

我可以使用 bind:

来解决这个问题
function MyClass(opts){
  this.options = opts;

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

但不完全确定为什么 bind 是必要的; .then() 杀死 this 了吗?

函数获取其上下文 (this) 的一种方式是从调用它们的对象中获取(这就是为什么 method1 具有正确的上下文 - 它是在 this 上调用的) .您正在将函数本身的引用传递给 then。你可以想象 then 的实现看起来像这样:

function then( callback ) {

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

在该示例中,callback 是对您的函数的引用。它没有任何上下文。正如您已经注意到的那样,您可以通过在将函数传递给 then 之前将函数绑定到上下文来解决这个问题。

this 始终是调用该方法的对象。但是,当将方法传递给 then() 时,您并没有调用它!该方法将存储在某个地方并稍后从那里调用。如果你想保留 this,你必须这样做:

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

或者如果你必须以 ES6 之前的方式来做,你需要在

之前保留 this
var that = this;
// ...
.then(function() { that.method2() })

默认情况下,在全局对象 (window) 的上下文中调用 Promise 处理程序。在严格模式下 (use strict;),上下文是 undefined。这就是 method2method3.

正在发生的事情
;(function(){
  'use strict'
  Promise.resolve('foo').then(function(){console.log(this)}); // undefined
}());

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

对于 method1,您将 method1 称为 this.method1()。这种调用它的方式在作为您的实例的 this 对象的上下文中调用它。这就是为什么 method1 中的上下文是实例。

基本上,您向它传递了一个没有上下文引用的函数引用。 this 上下文通过几种方式确定:

  1. 隐含地。调用全局函数或没有绑定的函数假定全局上下文。*
  2. 通过直接引用。如果您调用 myObj.f(),那么 myObj 将成为 this 上下文。**
  3. 手动装订。这是您 class 的功能,例如 .bind.apply。这些你明确说明 this 上下文是什么。这些总是优先于前两个。

在您的示例中,您正在传递一个函数引用,因此在调用它时暗示它是一个全局函数或没有上下文的函数。使用 .bind 通过创建一个明确设置 this 的新函数来解决此问题。

*这仅适用于 non-strict 模式。在严格模式下,this 设置为 undefined.

**假设您使用的函数没有被手动绑定。