What is the mechanism for lexical scope binding inside object methods (Javascript)?

Viewed 128

I'm experiencing a major brain freeze here understanding arrow functions and lexical scope: I read through all of Kyle Simpson's 'You don't know JS' some time ago, and I thought I understood scope and context pretty well. But I'm just not understanding how arrow function lexical scope applies to objects -- I've been working with classes in React, and there it's always clear that the this that the arrow function bound to was the class.

For instance, in this example (from https://medium.com/tfogo/advantages-and-pitfalls-of-arrow-functions-a16f0835799e)

 let obj = {
      myVar: 'foo',

      myFunc: function() { 
        console.log(this.myVar)  

        setTimeout(() => {
          console.log(this.myVar)
        }, 1000)
      }
    }

The arrow function should bind this correctly, obviating doing a bind. But this is from lexical scope? In what sense? Because the next scope is that of the function myFunc and this function's lexical parent scope is obj?

But then they give this example

let obj = {
  myVar: 'foo',

  myFunc: () => { 
    console.log(this.myVar)  
  }
}
obj.myFunc() // undefined

where correct binding of this does not happen. Is this because there is no function surrounding the arrow function, and so the next lexical scope is the global context?

To sum it up: is it true that the 'lexical scope' here is simply that same lexical scope defined by functions, and that this with arrow functions is simply to look for the parent of the next containing function?

I promise you I understood this (or thought I did) a while ago, but 'use it or lose it', I guess --

3 Answers

But this is from lexical scope? In what sense?

In the sense that it is the same value as this has inside the function where the arrow function is defined.

Both console.log(this.myVar) use the same this

Because the next scope is that of the function myFunc and this function's lexical parent scope is obj?

myFunc's parent scope is irrelevant. It isn't an arrow function. It gets its this value from where it is called.


where correct binding of this does not happen.

this is the same as the parent function. The object literal has no effect on it.

If we add another console.log, then the two are (again) referencing the same value.

console.log(this.myVar)
let obj = {
  myVar: 'foo',

  myFunc: () => { 
    console.log(this.myVar)  
  }
}

First keep in mind the definition of arrow function

An arrow function does not have its own this; the this value of the enclosing execution context is used (mozilla)


let obj = {
  myVar: 'foo',
  myFunc: function() { 
    console.log(this.myVar)  
    setTimeout(() => {
      console.log(this.myVar)
    }, 1000)
  }
}
obj.myFunc();

The arrow function should bind this correctly, obviating doing a bind. But this is from lexical scope? In what sense? Because the next scope is that of the function myFunc and this function's lexical parent scope is obj?

here the enclosing function of the setTimeout callback is a method on the object itself so it will inherit the current instance, as you said.


let obj = {
  myVar: 'foo',
  myFunc: () => { 
    console.log(this.myVar)  
  }
}
obj.myFunc() // undefined

where correct binding of this does not happen. Is this because there is no function surrounding the arrow function, and so the next lexical scope is the global context?

in this second scenario the method declared on the instance uses an arrow function so the this will be inherited from the enclosing function but since no function wrap your object will be Window

An arrow function has the same lexical-scoping rules as all other functions, except when it comes to this. When you define an arrow function, it captures what this is right now, at definition time, and uses that forever. Whichever value is available as thisin the lexical context of the arrow function's definition is the permanent value ofthis` within the arrow function.

One way to think about it: if I ended this arrow function definition, and the immediately used this outside the definition, what this value would that be?

In your second case, consider one additional property:

let obj = {
  myVar: 'foo',

  myFunc: () => { 
    console.log(this.myVar)  
  },

  myOtherVar: this.myVar
}

We definitely except myOtherVar to be undefined, because as you're building this object, this is window which has no myVar property. It's just the same for the arrow function: it permanently adopts a this of window, so this.myVar is undefined.

Related