Arrow functions retain the this
of the surrounding execution context from when they were declared. They don't change this
all the time, like normal methods do.
In your example, there is no execution context surrounding foo
, so this
is undefined
. This is the same behavior as a function declared using the function
keyword at the same scope and called in the same way. You can test that with:
let foo = () => { return this; }
console.log(foo() === undefined);
From the spec at 14.2.16:
Any reference to arguments, super, or this within an ArrowFunction must resolve to a binding in a lexically enclosing environment. Typically this will be the Function Environment of an immediately enclosing function.
(emphasis mine)
Interestingly, when that arrow function is present in the global scope, the BabelJS transpiler simply outputs
"use strict";
var foo = function foo() {
return undefined;
};
as if that were the only correct behavior. Reading the spec, it doesn't seem quite as strict, but this does seem like the right thing to do.
It appears that you can end up with the global object as this if you manage to run ES6 code using arrows without modules. Per the spec at 10.2.1:
- Global code is strict mode code if it begins with a Directive Prologue that contains a Use Strict Directive (see 14.1.1).
- Module code is always strict mode code.
So it is possible to get ES6 code in a non-strict context. If that happens, then this
will use the classical fallback and be set to window
(in the spec at 9.2 as an undefined [[ThisMode]]
).
In this example, there is no immediately enclosing function, thus no lexical scope to pick up, so this
ends up undefined.
In your second example, the capture on this
doesn't make a difference:
let o = {
foo: function() {
var self = this;
console.log(self);
},
bar() {
console.log(this);
}
};
The var self
statement is within the function, so it won't do anything. If you were to do:
let o = {
foo: function() {
var self = this;
return function() {
console.log(self);
}
}(),
bar() {
console.log(this);
}
};
then it will have the behavior you expect (roughly), although this
will still be undefined (or the global object) because you're not within a lexical environment to be captured.
If you were to use
class Foo {
bar() {
let baz = () => { return this; }
}
}
then bar
could be transpiled down to
function bar() {
var _this = this;
var baz = function baz() {
return _this;
};
}
which does what you want. That only works because there is a surrounding context to be captured.