I found that 'this' keyword seems always point to global
in using arrow function inside a nested object literal.
According to other questions, the following snippet can be explained as an arrow function's 'this' is defined in lexical context.
var c = 100;
var a = {c:5 , fn: () => {return this.c;} };
console.log(a.c); //100
However, I cannot understand the following code (nested object literal):
var c = 100;
var a = {
c: 5,
b: {
c: 10,
fn: ()=> {return this.c;}
}
}
console.log(a.b.fn());// still 100, why not 5?
I mean, if consider from the lexical context aspect, shouldn't the 'this' in a.b.fn point to a?
Why, no matter how many levels the object is nested, do all of the 'this' instances point to window or global?
An arrow function doesn't have its own this value and the arguments object. Therefore, you should not use it as an event handler, a method of an object literal, a prototype method, or when you have a function that uses the arguments object.
In short, with arrow functions there are no binding of this . In regular functions the this keyword represented the object that called the function, which could be the window, the document, a button or whatever. With arrow functions the this keyword always represents the object that defined the arrow function.
An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new. target keywords. Arrow function expressions are ill suited as methods, and they cannot be used as constructors.
Arrow functions cannot be used to write object methods because, as you have found, since arrow functions close over the this of the lexically enclosing context, the this within the arrow is the one that was current where you defined the object.
The only expression in JavaScript that changes scope is a function and, as of ES6, blocks (note that an object literal is not a block, despite having curly braces around it). This means: everything that is not inside a function is in global scope.
In global scope, this
refers to the global object (window
in case of browsers). The only thing that changes scope is the arrow function (yes, they DO change scope!) - but it binds this lexically (which means, it uses the this
from the outer scope), so it's still the global object.
If you want this
to refer to the a
object, use an IIFE instead of an object literal:
var c = 100;
var a = new function () {
this.c = 5;
this.b = {
c: 10,
fn: ()=> {return this.c;}
}
}()
alert(a.b.fn()) // 5;
Or, to bind b
to this
:
var c = 100;
var a = {
c : 5,
b : new function () {
this.c = 10;
this.fn = ()=> {return this.c;}
}()
}
alert(a.b.fn()) // 10;
Alternatively, to bind this
to b
, you can also use a regular function instead of the arrow function:
var c = 100;
var a = {
c: 5,
b: {
c: 10,
fn: function () {return this.c;}
}
}
alert(a.b.fn()) // 10;
It's the same as this
where the object initializer is. So in both of your examples, it's the same as this
where your var a = ...
line is. this
never changes within a given execution context, and object initializers don't create a new execution context; only functions and eval
do that. In your examples, the only time a new execution context is created is when you call fn
, and since fn
is an arrow function, it closes over the this
in the execution context where it was created (which happens to be the global execution context in your examples).
The reason you see 100
for this.c
in your example code is that this
at global scope (in loose mode) refers to the global object, and var
variables at global scope become properties of the global object, and so this.c
is the c
global variable.
If you put all of that code in a scoping function, like this:
(function() { // Or `(() => {`, doesn't matter in this case
var c = 100;
var a = {
c: 5,
b: {
c: 10,
fn: ()=> {return this.c;}
}
}
console.log(a.b.fn());// still 100, why not 5?
})();
...this.c
would be undefined
, because although this
would still refer to the global object, c
would no longer be a global variable (and thus a property of the global object).
If you want this
inside fn
to refer to b
in the expression a.b.fn()
, then you don't want an arrow function there, you want a normal function; you'd get 10
(the value of a.b.c
), not 5
(the value of a.c
).
Of course, as this is a one-off object and fn
closes over a
, you can also just make fn
's body return a.c;
or return a.b.c;
depending on which c
you want.
Another way to think about this is that there is no notion of a new "this" scope inside the literal.
A function, on the other hand, would introduce a new scope.
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