0

Here is my code:

class Employee {
  constructor(ename) {
    this.ename = ename;
  }
}

class EmployeeRenderer {
  constructor(employees) {
    this.employees = employees;
    this.ename = "EmployeeRenderer";
  }
  renderWithArrowFunc() {
    this.employees.forEach(emp => {
      console.log(this.ename); // Will print EmployeeRenderer 3 times
    })
  }
}


var employees = [
  new Employee('Alex'),
  new Employee('Bob'),
  new Employee('Smith')
];

var employeeRenderer = new EmployeeRenderer(employees);
employeeRenderer.renderWithArrowFunc();

As we know, in an arrow function, this is not a declared variable, so to resolve a reference to this, JavaScript consults with the enclosing scope(s). As such, in the above code, when console.log(this.ename) is being executed, JavaScript asks the first immediate enclosing lexical scope- the function forEach- about this. In forEach implementation** , this points at the value of the array on which the function has been called: employees and as it doesn't have ename property, so I expected to see undefined 3 times in the output than EmployeeRenderer. It shows that this has been resolved to EmployeeRenderer.ename. What am I missing here?

** I searched for an implementation of forEach and couldn't find one, hence I assume it must be identical to the pollyfill mentioned in MDN.

Hans
  • 2,674
  • 4
  • 25
  • 48
  • 3
    I converted your code to a snippet and it logs out `undefined`, `undefined`, `undefined` – Nicholas Tower Jul 13 '19 at 19:21
  • @NicholasTower Oops, I made too many mistakes. Fixed and updated the question. Thanks. – Hans Jul 13 '19 at 19:30
  • "*the first immediate enclosing lexical scope - the function `forEach`*" - no, a function call does not introduce a **lexical scope**. And certainly not one to the *implementation* of the called function. – Bergi Jul 13 '19 at 19:30
  • @Bergi But `forEach` has a declaration anyway so it a nested scope is created for it. Am I wrong? – Hans Jul 13 '19 at 19:46
  • But in your code, `forEach` is not declared, and the arrow function is not defined within the implementation of `forEach`. It's just passed as an argument to a call. – Bergi Jul 13 '19 at 19:49

2 Answers2

2

Variable scope is lexical. The code of forEach is not relevant to determining the scope of variables in the callback function. It's just the code that textually contains the arrow function definition, and the blocks around that, and so on.

So this refers to the context that was used to call returnWithArrowFunct(), which is the value of the employeeRenderer variable.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • To be more precise, it's just the code that textually contains the *definition of the arrow function* (which is usually as the argument to the `forEach` call, but not necessarily). – Bergi Jul 13 '19 at 19:28
  • True, I was referring to this specific example code, where they're the same thing. But I've edited it. – Barmar Jul 13 '19 at 19:42
  • I still don't get it. How is `this` resolved? Isn't it resolved *lexically*? – Hans Jul 13 '19 at 19:42
  • When you call a function in the form `x.y()`, the value of `x` becomes the `this` context inside the function `y`. Then when calling the arrow function, it's resolved lexically, so it retains the binding from `y`. – Barmar Jul 13 '19 at 19:46
  • @Hans Yes. [Lexical scoping](https://en.wikipedia.org/wiki/Lexical_scope#Lexical_scoping) means that it doesn't look into the `forEach` implementation from which it was *called*. – Bergi Jul 13 '19 at 19:46
  • @Bergi Exactly! So `this.employees` becomes the value of `this` inside the function `forEach`, right? And when the arrow function asks for `this`, what should be returned? – Hans Jul 13 '19 at 19:49
  • The arrow function isn't defined inside the definition of `forEach`, so it doesn't get its scope from it. Basically, if you can't see the source code of a function, it's not in the scope chain. – Barmar Jul 13 '19 at 19:51
  • `this` inside the arrow function is the same as `this` outside the call to `forEach`. – Barmar Jul 13 '19 at 19:51
  • It's just like any other local variable inside `renderWithArrowFunc`. That's one of the things that makes arrow functions different from traditional functions. – Barmar Jul 13 '19 at 19:52
  • @Hans Yes, `employees` becomes the `this` context in the `employees.forEach()` call (i.e. in the body of the `forEach` method), but that's not the `this` value we are talking about - it's irrelevant for the arrow function because the arrow function is not defined in the body of the `forEach` method. – Bergi Jul 13 '19 at 19:52
  • 1
    @Hans I suggest you read https://stackoverflow.com/questions/500431/what-is-the-scope-of-variables-in-javascript – Barmar Jul 13 '19 at 19:53
  • 1
    @Bergi Now I get it! Function call chain has nothing to do with lexical scopes. The latter is just about function declaration. – Hans Jul 13 '19 at 20:13
1

this has no property ename and points to the outer this. For getting a value, you need to take emp.ename

class Employee {
  constructor(ename) {
    this.ename = ename;
  }
}

class EmployeeRenderer {
  constructor(employees) {
    this.employees = employees;
  }
  renderWithArrowFunc() {
    this.employees.forEach(emp => {
      console.log(emp.ename); // Will print Alex, Bob, Smith
    })
  }
}


var employees = [
  new Employee('Alex'),
  new Employee('Bob'),
  new Employee('Smith')
];

var employeeRenderer = new EmployeeRenderer(employees);
employeeRenderer.renderWithArrowFunc();
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392