web-dev-qa-db-ja.com

Promiseを使用するときに、クラスメソッド内で「this」が未定義なのはなぜですか?

Javascriptクラスがあり、各メソッドはQ promiseを返します。 thismethod2およびmethod3で未定義である理由を知りたい。このコードを書くためのより正しい方法はありますか?

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を削除していますか?

82
SteamDev

thisは、常にメソッドが呼び出されるオブジェクトです。ただし、メソッドをthen()に渡すときは、呼び出していません!メソッドはどこかに保存され、後でそこから呼び出されます。 thisを保持する場合は、次のようにする必要があります。

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

または、ES6以前の方法で行う必要がある場合は、thisを保存する必要があります。

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

Promiseハンドラーは、デフォルトでグローバルオブジェクト(window)のコンテキストで呼び出されます。厳格モード(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の場合、method1this.method1()として呼び出しています。この呼び出し方法は、インスタンスであるthisオブジェクトのコンテキストで呼び出します。 method1内のコンテキストがインスタンスである理由です。

18
Joseph

基本的に、コンテキスト参照のない関数参照を渡します。 thisコンテキストは、いくつかの方法で決定されます。

  1. 暗黙的に。グローバル関数またはバインディングなしの関数の呼び出しは、グローバルコンテキストを前提としています。*
  2. 直接参照による。 myObj.f()を呼び出すと、myObjthisコンテキストになります。**
  3. 手動バインディング。これは、.bind.applyなどの関数のクラスです。これらはthisコンテキストが何であるかを明示的に述べています。これらは常に前の2つよりも優先されます。

この例では、関数参照を渡しているため、呼び出しの際には、グローバル関数またはコンテキストのない関数であることが暗示されています。 .bindを使用すると、thisが明示的に設定されている新しい関数を作成することでこれを解決します。

*これは、非厳密モードでのみ当てはまります。厳格モードでは、thisundefinedに設定されます。

**使用している関数が手動でバインドされていないと仮定します。

2
Mike Cluck

関数がそのコンテキスト(this)を取得する1つの方法は、それらが呼び出されるオブジェクトからです(そのため、method1には正しいコンテキストがあります-thisで呼び出されます)。関数自体への参照をthenに渡します。 thenの実装は次のように見えると想像できます。

function then( callback ) {

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

その例では、callbackは関数への参照です。コンテキストはありません。既に述べたように、それを渡す前に関数をコンテキストにバインドすることで、それを回避できます。

0
James Allardice