J'ai une classe javascript et chaque méthode renvoie une promesse Q
. Je veux savoir pourquoi this
est indéfini dans method2
et method3
. Existe-t-il une manière plus correcte d'écrire ce code?
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;
};
Je peux résoudre ce problème en utilisant bind
:
function MyClass(opts){
this.options = opts;
return this.method1()
.then(this.method2.bind(this))
.then(this.method3.bind(this));
}
Mais pas tout à fait sûr pourquoi bind
est nécessaire; Est-ce que .then()
tuant this
est éteint?
this
est toujours l'objet sur lequel la méthode est appelée. Cependant, lorsque vous passez la méthode à then()
, vous ne l'appelez pas! La méthode sera stockée quelque part et appelée plus tard. Si vous voulez conserver this
, vous devrez le faire comme ceci:
.then(() => this.method2())
ou si vous devez le faire de manière antérieure à ES6, vous devez conserver this
avant:
var that = this;
// ...
.then(function() { that.method2() })
Les gestionnaires de promesse sont appelés par défaut dans le contexte de l'objet global (window
). En mode strict (use strict;
), le contexte est undefined
. C'est ce qui arrive à method2
et method3
.
;(function(){
'use strict'
Promise.resolve('foo').then(function(){console.log(this)}); // undefined
}());
;(function(){
Promise.resolve('foo').then(function(){console.log(this)}); // window
}());
Pour method1
, vous appelez method1
en tant que this.method1()
. Cette façon de l'appeler l'appelle dans le contexte de l'objet this
qui est votre instance. C'est pourquoi le contexte à l'intérieur de method1
est l'instance.
En gros, vous lui transmettez une référence de fonction sans référence au contexte. Le contexte this
est déterminé de plusieurs manières:
myObj.f()
, alors myObj
sera le contexte this
. **.bind
et .apply
. Celles-ci, vous indiquez explicitement ce qu'est le contexte this
. Celles-ci ont toujours priorité sur les deux précédentes.Dans votre exemple, vous passez une référence de fonction. Ainsi, lors de son invocation, il est implicite d'être une fonction globale ou sans contexte. Utiliser .bind
résout ce problème en créant une nouvelle fonction dans laquelle this
est explicitement défini.
* Ceci n'est vrai qu'en mode non strict. En mode strict, this
est défini sur undefined
.
** En supposant que la fonction que vous utilisez n'ait pas été liée manuellement.
Une façon dont les fonctions obtiennent leur contexte (this
) provient de l'objet sur lequel elles sont appelées (c'est pourquoi method1
a le bon contexte - il est invoqué sur this
Vous passez une référence à la fonction elle-même à then
. Vous pouvez imaginer que l'implémentation de then
ressemble à ceci:
function then( callback ) {
// assume 'value' is the recently-fulfilled promise value
callback(value);
}
Dans cet exemple, callback
est une référence à votre fonction. Il n'a pas de contexte. Comme vous l'avez déjà noté, vous pouvez contourner ce problème en liant la fonction à un contexte avant de le transmettre à ce dernier.