skip to Main Content

I’ve reading the MDN docs, and I can truly say this is the first time in a while I have been baffled.

Refer to this code:

for (
  let i = 0, getI = () => i, incrementI = () => i++;
  getI() < 3;
  incrementI()
) {
  console.log(i);
}
// Logs 0, 0, 0

I see the 1st iteration:

  • for loop initializes i to 0
  • creates a closure over i
  • creates a closure over i++
  • calls getI function and returns 0, executes the condition to continue, the loop executes console.log(i)
    • Is a new lexical scope created after console.log(i)? Does the initial i variable disappear?
    • does getI function get defined for each new iteration?
  • calls incrementI and performs i++
    • Is a new i variable created, the previous i copied to the new i and then the new i incremented?
  • next iteration begins
    • If a new i is generated with the new incremented value, why does console.log(i) still refer to the initial i, which is 0?

I read the documentation in MDN about for, however I could not find explanation of the behaviour shown in the code snippet. Why does it log 0, 0, 0 instead of 0, 1, 2?

2

Answers


  1. I you want make that function work, just put initialization of i outside for.

    let i;
    for (
      i = 0, getI = () => i, incrementI = () => i++;
      getI() < 3;
      incrementI()
    ) {
      console.log(i);
    }
    Login or Signup to reply.
  2. The answer lies in this part of the docs

    The scoping effect of the initialization block can be understood as if the declaration happens within the loop body, but just happens to be accessible within the condition and afterthought parts. More precisely, let declarations are special-cased by for loops — if initialization is a let declaration, then every time, after the loop body is evaluated, the following happens:

    • A new lexical scope is created with new let-declared variables.
    • The binding values from the last iteration are used to re-initialize the new variables.
    • afterthought is evaluated in the new scope.

    Ie, after each iteration of the loop a new i is declared with the value of the previous one. Then the afterthought is evaluated. But as in your case, getI and incrementI are closed over the original i this original i is incremented and checked (so the loop eventually stops) but the next call to console.log always uses the "new" i, which is initialized to 0 (ie the value of in the previous iteration before the afterthought was evaluated) and then never incremented.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search