Почему метод '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
выключено?
Ответы
Ответ 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
является ссылкой на вашу функцию. Это не имеет никакого контекста. Как вы уже заметили, вы можете обойти это, связав эту функцию с контекстом, прежде чем передать его затем.