Why does Promise.then
passes execution context of undefined
when using a class method as callback, and window
when using a "normal function"?
Is the class method detached from its owning object/class? and why undefined
and not window
?
function normal() {
console.log('normal function', this);
}
const arrow = () => {
console.log('arrow function', this);
}
function strictFunction() {
'use strict';
console.log('strict function', this);
}
class Foo {
test() {
this.method(); // Foo
Promise.resolve().then(() => console.log('inline arrow function', this)); // Foo
Promise.resolve().then(normal); // window
Promise.resolve().then(arrow); // window
Promise.resolve().then(strictFunction); // undefined
Promise.resolve().then(this.method); // undefined <-- why?
}
method() {
console.log('method', this);
}
}
const F = new Foo();
F.test();
(jsFiddle)
I would expect the context of this.method
to be lost but cannot understand why the different behavior between this.method
and "normal" and arrow functions.
Is there a spec for this behavior? The only reference I found was Promises A+ refering to that "in strict mode this
will be undefined
inside; in sloppy mode, it will be the global object
.".
The quote you have there tells you why:
in strict mode
this
will be undefined inside; in sloppy mode, it will be the global object.
The ES6 spec says that:
All parts of a ClassDeclaration or a ClassExpression are strict mode code
Therefore, because of strict mode, this
within an unbound class method, will be undefined
.
class A {
method() {
console.log(this);
}
}
const a = new A();
a.method(); // A
const unboundMethod = a.method;
unboundMethod(); // undefined
This is the same behavior you would get if you passed a normal function with strict mode because this
binding is undefined
by default in strict mode, not set to the global object.
The reason normal
and arrow
have this
as window
is because they are not within the class and thus not wrapped in strict mode.
As far as promises and the then
method, it will just pass undefined
as this
but won't override already bound this
.
If you look at the PromiseReactionJob spec:
The job PromiseReactionJob with parameters reaction and argument applies the appropriate handler to the incoming value, and uses the handler's return value to resolve or reject the derived promise associated with that handle.
...
let handlerResult be Call(handler, undefined, «argument»).
The second argument to Call is the this
value, which is set to undefined
.