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
.".
Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.
They can handle multiple asynchronous operations easily and provide better error handling than callbacks and events. In other words also, we may say that, promises are the ideal choice for handling multiple callbacks at the same time, thus avoiding the undesired callback hell situation.
A callback function is passed as an argument to another function whereas Promise is something that is achieved or completed in the future. In JavaScript, a promise is an object and we use the promise constructor to initialize a promise.
Promises Can Be Paused Just because you are already executing inside a then() function, doesn't mean you can't pause it to complete something else first. To pause the current promise, or to have it wait for the completion of another promise, simply return another promise from within then() .
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
.
This has nothing to do with Promises, but rather the context in which this
is called.
Case 1:
this.method(); // Foo
Here method
is a function defined within the Foo
class, so this
is evaluated as the object that triggered the function, which is this
in this.method
. Hence - Foo
is displayed.
Case 2:
Promise.resolve().then(() => console.log('inline arrow function', this)); // Foo
Arrow functions are a feature of ES6, whose unique property is that the this
context in the enclosing context where it is defined. The function was called in a context where this === Foo
so that's what's displayed.
Case 3:
Promise.resolve().then(normal); // window
Promise.resolve().then(arrow); // window
The arrow function retains its context as the window since it's an arrow function, and the normal function is evaluated without a context, in which this
is evaluated to window when not in strict mode
.
Case 4:
Promise.resolve().then(strictFunction); // undefined
Since strict mode
is requested within the body of this function, which is declared on the window, this
is evaluated to undefined.
Case 5:
Promise.resolve().then(this.method); // undefined <-- why?
In this spec, it is defined that all Class code is strict code:
All parts of a ClassDeclaration or a ClassExpression are strict mode code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With