Подтвердить что ты не робот

Почему метод 'this' undefined внутри класса при использовании promises?

У меня есть класс javascript, и каждый метод возвращает обещание Q. Я хочу знать, почему this undefined в method2 и 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 выключено?

4b9b3361

Ответ 1

this всегда является объектом, на который вызывается метод. Однако, передавая метод then(), вы его не называете! Метод будет храниться где-нибудь и вызывается оттуда позже. Если вы хотите сохранить this, вам нужно будет сделать это следующим образом:

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

или если вам нужно сделать это до ES6, вам нужно сохранить this до:

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

Ответ 2

Обработчики Promise вызывается в контексте глобального объекта (window) по умолчанию. Когда в строгом режиме (use strict;), контекст undefined. Это то, что происходит с method2 и method3.

;(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 является экземпляром.

Ответ 3

В принципе, вы передаете ему ссылку на функцию без ссылки на контекст. Контекст this определяется несколькими способами:

  • Косвенно. Вызов глобальной функции или функции без привязки предполагает глобальный контекст. *
  • Прямая ссылка. Если вы вызываете myObj.f(), тогда myObj будет <<20 > . **
  • Ручное связывание. Это ваш класс функций, таких как .bind и .apply. Они явно указывают, что такое контекст this. Они всегда имеют приоритет над предыдущими двумя.

В вашем примере вы передаете ссылку на функцию, поэтому при вызове она подразумевается как глобальная функция или одна без контекста. Использование .bind разрешает это, создавая новую функцию, в которой this явно задано.

* Это верно только в нестрогом режиме. В строгом режиме this устанавливается на undefined.

** Предполагая, что используемая вами функция не связана вручную.

Ответ 4

Односторонние функции получают свой контекст (this) от объекта, на котором они вызывается (поэтому method1 имеет правильный контекст - он вызывается на this). Вы передаете ссылку на функцию непосредственно на then. Вы можете себе представить, что реализация then выглядит примерно так:

function then( callback ) {

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

В этом примере callback является ссылкой на вашу функцию. Это не имеет никакого контекста. Как вы уже заметили, вы можете обойти это, связав эту функцию с контекстом, прежде чем передать его затем.