1

I'm working through a JavaScript tutorial. To show the difference between let and var, the following example is given, where it generates a bunch of boxes, and if you click on a box, it outputs the index of the box:

I created this codepen: https://codepen.io/run_the_race/pen/aXJQmp?editors=1111 The main part of interest is:

for (var i=0; i < 20; i++)
{
    var div = document.createElement("div");
    div.onclick = function() {
        console.log("You clicked on box #:" + i);
    }
    document.querySelector("section").appendChild(div);
}

If one uses var to declare i, the same index value is shared between all the boxes. If one uses let to declare i, then the tutorial says i has block scope, so a different variable is created for each index.

Well my question is then, if i is a separate variable in each loop execution, how come if I increment i within the loop, it will change the number of iterations?

It appears to be separate for each iteration (not shared like when declared with var), but is shared because changing it affects the iterations. I understand what is happening, but I don't understand why.

Edit: Although the other posts address what the tutorial explained, they did not explain that for each iteration, i is declared again with the value of i at the end of the previous iteration, as @Pointy explained. I also simplified it a bit.

run_the_race
  • 1,344
  • 2
  • 36
  • 62
  • 1
    On each iteration, the new variable is initialized to the value of the "previous generation" variable. – Pointy Feb 01 '19 at 13:49
  • 1
    Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – marekful Feb 01 '19 at 13:49
  • @marekful I'm not sure this is a duplicate of that, though there may be an answer in there that explains this particular aspect of how `let` variables in `for` loops work. – Pointy Feb 01 '19 at 13:50
  • I was going to quote the spec but it's unreadably complicated. It's all in section 13.7.4.7 I think. – Pointy Feb 01 '19 at 13:57
  • @Pointy thank you, that makes a lot sense! – run_the_race Feb 01 '19 at 14:01
  • @run_the_race I expanded the explanation a bit in an answer. It is somewhat weird if you take the time to think about it, as you have :) – Pointy Feb 01 '19 at 14:03

2 Answers2

2

The first expression in the for loop can declare one or more variables with let, which is called a lexical declaration in the spec. The variables exist in a context created for the block of statements that comprises the body of the loop. On the first iteration, the variables start with whatever their initializer expressions evaluate to, or undefined.

After the third expression of the for loop header is evaluated at the end of the first iteration, a new context is created for the block, and it will contain all the same variables declared in the lexical declaration. The values of each variable from the old context are copied into the new one before the block execution is started for the second iteration. That process happens on each iteration until the loop is finished.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 1
    I haven't had enough coffee yet this morning to investigate, but I'm wondering what happens when the `for` loop has just a single statement and no `{ }` block. – Pointy Feb 01 '19 at 14:05
1

Try this, this will secure that the variable received by the onclick function is the same at the moment of creation:

for (var i=0; i < 20; i++)
{
    console.log(i);
    var div = document.createElement("div");
    var str = "You click on box #:" + parseInt(i);
    (str => { 
        div.onclick = function() {
             console.log(str);
        }
    })(str)
    document.querySelector("section").appendChild(div);
}
Alberto
  • 1,423
  • 18
  • 32
  • This will certainly work, but the OP's question is not really about this topic. The question is about the relationship between the `let` variables of one loop iteration with the next iteration. – Pointy Feb 01 '19 at 13:56
  • @Pointy Sorry I just wanted to give him a solution. – Alberto Feb 01 '19 at 13:58
  • 1
    No that's fine, your solution does work. The OP could have simplified the question a lot more :) – Pointy Feb 01 '19 at 13:59
  • Very clever, took me a while to understand. I see you are calling a function, passing the argument by reference so its a new copy. – run_the_race Feb 01 '19 at 14:03