1

In this classic interview question, a new i is created for each iteration. This new i has closure with the function in setTimeout and produces the output 0 1 2 3 4. I don't understand why a new i is initialized when it's declared with let but not when it's declared with var. That should be impossible since let doesn't allow another variable with the same name to be created. Also, since closures are created when a function is defined, shouldn't the anonymous function in setTimeout still have closure around the original i equal to 0 in the block scope? Thanks.

for (let i = 0; i < 5; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000);
}
//Output: 
//0 
//1
//2
//3
//4
John Jacob
  • 77
  • 1
  • 9
  • what result did you expect? `0, 0, 0, 0, 0`? – GrafiCode Jun 03 '22 at 10:50
  • I am not sure if i understood the paragraph above the code. But for every iteration, there is a new setTimeout that gets added to 'macrotasks' queue with a different value of 'i' by virtue of block scoping nature of 'let'. And once the for loop is done executing, the macrotasks are executed, so each 'i' is different. – pixlboy Jun 03 '22 at 10:55
  • After reading the blog post, I believe I understand. This behavior is specially created for `for` loops with `let` declarations. The binding is dynamically reassigned at runtime and this will not occur with `var` or `const`. – John Jacob Jun 03 '22 at 11:05
  • I don't think it matters that the let variable is block scoped. The anonymous function that setTimeout sends to the Web API has closure with `i` whether its declared with `let` or `var`. If they did not make a special interaction between for loops and let declarations so there's a fresh binding for each iteration, the `async` function would be bound to the wrong value. – John Jacob Jun 03 '22 at 11:33
  • To understand this you need to understand blocks, closures and scopes. I really recommend to read the whole book: "You Don't Know JS: Scope & Closures": https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/scope%20&%20closures/README.md#you-dont-know-js-scope--closures . There is also a part that explains this case: https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/scope%20%26%20closures/ch5.md#loops--closure – wawka Jun 03 '22 at 14:38
  • I think I understand blocks and closures pretty well, which is why the standard explanation didn't make sense. That explanation is good but doesn't mention re-binding. The re-declaration is important, but the asynchronous function only has a fresh binding to the re-declared variable due to the behavior of let declarations within for loops (talked about here https://2ality.com/2015/02/es6-scoping.html). Closure is formed where the function is defined so without this dynamic re-binding there will be stale closure. I believe my understanding is correct but always open to new info. – John Jacob Jun 03 '22 at 15:09

0 Answers0