0

Why is the output of the following code :

let nodes = [];
for (let i = 0; i < 5; ++i) {
    nodes[i] = () => i;
}
for (let node of nodes) {
    console.log(node());
}

is 0 1 2 3 4, while the output of the following code :

let nodes = [];
let i;
for (i = 0; i < 5; ++i) {
    nodes[i] = () => i;
}
for (let node of nodes) {
    console.log(node());
}

is 5 5 5 5 5?

  • 2
    If you declare `i` outside the loop then there is only a single variable `i`. If you declare it in side the header a new scope/variable is created for every iteration. Arrow functions are irrelevant in this example. – Felix Kling Mar 09 '20 at 11:49

2 Answers2

3

let variables are scoped the to the block you use it in.

If you use let outside the for block, then you create one variable shared by each function you create (and you change the value of that variable each time you go around the loop).

If you use let inside it, you create a new variable each time you go around the loop, so each function has its own variable with a value that you aren't changing.

How do closure and environment context come into play in this?

They don't really. It's just a matter of if each function closed over the same i or a different i … and you aren't using this.

The type of function you use is irrelevent. You'd get the same effect with a function expression.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • Thank you for your answer. My friend also suggested the same ideas, but I don't have any means of verifying this. Do you have any outside references or simple code demonstrating this behavior? – Bimo Adityarahman Mar 09 '20 at 11:51
  • 1
    @BimoAdityarahman: Uh? Your example already demonstrates this behavior. What else are you looking for? – Felix Kling Mar 09 '20 at 11:59
  • @FelixKling I was hoping to see another example, preferably simpler with the same behavior as I have no idea how to achieve this without using the setup like the code shown above. External references surely do just fine. I think this explanation can do with more supporting evidences as it seems apparently nontrivial and language specific, especially for me coming from another language like C++. Thank you in advance. – Bimo Adityarahman Mar 09 '20 at 12:18
  • @BimoAdityarahman: Depending on how closures work in other languages, this isn't that language specific. Even in C++, if you declare a variable before the loop, each iteration of the loop will access the same variable. Closures however can work in two different ways: Evaluate/capture free variables at declaration time or at call time. I don't know how C++ works in this regard. – Felix Kling Mar 09 '20 at 12:27
  • @BimoAdityarahman: I still don't quite now what exactly you want to see, but another example that demonstrates block scope would be `let x = 1; { let x = 2; console.log(x); }; console.log(x);` vs `let x = 1; { x = 2; console.log(x); }; console.log(x);` – Felix Kling Mar 09 '20 at 12:29
  • @FelixKling Thank you for your time. In C++ from what I know, references work differently than high level language like Javascript and Python, because references (lvalue reference) point to memory addresses of variables with a well defined lifetime corresponding to their scopes. I am interested in seeing another example that demonstrate how a new variable is created for each iteration, as I'm sure it's normal to assume that the scope of variable declared in loop initialization is until the loop ends, not per iterations. – Bimo Adityarahman Mar 09 '20 at 12:41
  • 2
    @BimoAdityarahman: Fair enough. The authoritative reference for this is of course the ECMAScript language specification, but it might not be easy to understand. It all starts at https://www.ecma-international.org/ecma-262/10.0/#sec-for-statement-runtime-semantics-labelledevaluation at the third production rule. And the algorithm that creates the per iteration environment is https://www.ecma-international.org/ecma-262/10.0/#sec-createperiterationenvironment – Felix Kling Mar 09 '20 at 13:09
0

Variables declared by let have their scope in the block for which they are defined, as well as in any contained sub-blocks.

In your first code, variable i is declared as local variable during iteration. But in the second code, i is declared as a global variable. So that it prints 5,5,5,5,5

Please refer this link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

Jeni
  • 320
  • 2
  • 12