1

How come the below code creates closures on different values of i? As far as I know closure is when a function remembers its lexical scope even when that function is executed outside of its lexical scope. When a loop is declared, let creates a block scope variable called i. But that declaration happens once. From that logic, I would expect this loop to log 5 5 5 5 5, but instead it logs 0 1 2 3 4. If I declared new variable inside the loop let j = i and pass that into console.log(j), then I would understand the output being 0 1 2 3 4 because each iteration creates a new block scoped variable and function closes on that. But how does the following closes on different i variable when only one is supposed to be created for the whole block scope? What am I missing here?

for(let i = 0; i<5;i++){
    setTimeout(function(){
        console.log(i);
    },i*1000);
}
neo
  • 1,952
  • 2
  • 19
  • 39
  • Declarations in the initialization part of for loops are magic for this reason. – Ry- Jun 01 '18 at 23:59
  • @Ry- Even so, declaration of i happens once. What I mean is there is one i in the lexical scope. Not 5 of them. – neo Jun 02 '18 at 00:02
  • What can I say? It’s magic. See the specific *LexicalDeclaration* variant of https://tc39.github.io/ecma262/#prod-IterationStatement, and step 11 of the corresponding https://tc39.github.io/ecma262/#sec-for-statement-runtime-semantics-labelledevaluation (*perIterationBindings* that reach ForBodyEvaluation and CreatePerIterationEnvironment). – Ry- Jun 02 '18 at 00:05
  • 3
    Every iteration creates a new scope and the previous value of `i` is copied over to the new scope (and incremented of course, not sure whether before or after). – Felix Kling Jun 02 '18 at 00:05
  • @FelixKling So every iteration is considered to be a new block scope? I thought the whole for loop was one block scope. – neo Jun 02 '18 at 00:07
  • As Ry said, loops are magical. loop + `let` or `const` in header = block scope per iteration. – Felix Kling Jun 02 '18 at 00:12
  • 1
    Paste the code into [Babel](https://babeljs.io/repl/) and it should become pretty clear. – CertainPerformance Jun 02 '18 at 00:14
  • @CertainPerformance Great advice. I pasted it and it actually created a function and had a parameter called i. There was a loop that called that function. Since function got passed an i, it created a new i in each iteration. The only question is if how babel transpiles is the way browser executes? – neo Jun 02 '18 at 00:19
  • *"The only question is if how babel transpiles is the way browser executes?"* Probably not. – Felix Kling Jun 02 '18 at 00:57

1 Answers1

1

There's a special behavior defined for let declarations used in the head of a for-loop. This behavior says that the variable will be declared not just once for the loop, but each iteration. And, it will, helpfully, be initialized at each subsequent iteration with the value from the end of the previous iteration.

You can take a reference from this URL: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch5.md

Jyoti Arora
  • 131
  • 8