11

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?

6
  • 4
    It points to the current scope. Objects don't change scope, only functions change scope. So the this inside of the arrow function will refer to the nearest function it is inside. In your case, that is just the top level. Commented Jun 5, 2016 at 14:46
  • 1
    @mash: this and scope are largely unrelated, and this only rarely "refers to ... [a] function." But the gist of the comment is, of course, correct. Commented Jun 5, 2016 at 14:56
  • Just for the record: arrow functions change scope just as any function. They just bind this, arguments, super and new.target lexically instead of defining their own variablesd that shadow the outer ones. Commented Jun 5, 2016 at 15:01
  • @JohannesH.: Just for the record :-), arrow functions don't bind this at all. (Or more accurately: The environment record created when we call them doesn't.) Which is why they inherit the this of the execution context in which they're created: They close over it, exactly like closing over anything else in that context (e.g., variables). Commented Jun 5, 2016 at 15:04
  • 1
    Related: Arrow function vs function declaration / expressions: Are they equivalent / exchangeable? Commented Jun 5, 2016 at 15:41

3 Answers 3

7

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 aobject, 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 bto 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;
Sign up to request clarification or add additional context in comments.

6 Comments

"The only expression in JavaScript that changes scope is a function" - nope. Blocks do as well.
@Bergi I agree with you and that's why I was confused about this in nested object literals.
@jt-wang: but object literals aren't blocks :-)
They do as of ES6, yes. However block scoping only affects variables declared with let and has no influence on var, so it's half-half. However, I will change my wording to indicate that.
Beware conflating scope and execution context. They're related, but very different. this relates to execution contexts, not scope.
|
5

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.

2 Comments

What about the second code snippet? The b is initialed inside a, but why does a.b.c points to global's c instead of a.c?
@jt-wang: I was talking about the second snippet. :-) this.c refers to the c global in that example for the same reason as the first: this is still what it was as of the var a line. this never changes within an execution context, and an object initializer doesn't create a new execution context (only functions and eval do).
2

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.