-4

In the following example, how does the for loop know that it has to assign a new value to each i and not just assign the last i three times likes var does?

for (let i = 0; i < 3; i++) {
    setTimeout(function() { console.log(i) }, 100);
}

Also, I've seen some explanations that mention let is block-scoped in a for loop.. But since let's lexical scope is the for loop (outside of the {block}), is it then "loop-scoped"? I've only heard of block/function/global scope, never a loop scope, so what exactly is a loop if not a function?

  • why do you think the scope of `i` is outside the block? Also, the loop is a loop, not a function. – evolutionxbox Jun 30 '22 at 12:17
  • Hi, because let i = 0 is declared before the curly brackets that indicate a block –  Jun 30 '22 at 12:18
  • 1
    With the example, `i` is not available outside the block. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for#lexical_declarations_in_the_initialization_block_is_scoped_to_the_for_loop – evolutionxbox Jun 30 '22 at 12:21
  • 1
    Yes, `i` is "loop scoped". `for` loop has some exceptional behavior of [scoping the variables](https://tc39.es/ecma262/multipage/ecmascript-language-statements-and-declarations.html#sec-runtime-semantics-forloopevaluation). – Teemu Jun 30 '22 at 12:22

2 Answers2

0

The actual scope is determined by the interpreter / runtime which ideally correctly implements sufficiently precise language specs (in this case see here).

The latter tell you that in 'for' loops, the scope of declarations in the initializer section is a 'virtual' block encompassing the overt block of the for loop. However, it gets a bit more complicated: for each iteration, the binding of declarations is renewed.

You could rewrite the code as follows to elicit equivalent behavior:

let i=42;
{
    let i_shadow = 0;
    while (i_shadow < 3) {
        let i = i_shadow;

        setTimeout(function() { console.log(`'i' written through 'setTimeout': ${i}`) }, 100);

        i_shadow++;
    }
    console.log(`'i' inside virtual block: ${i_shadow}.`);
}
console.log(`'i' outside virtual block: ${i}.`);

The interpreter/runtime 'knows' about that behavior as the context o a 'for' loop has been lexically established at the time the declaration of i is encountered (*).

Compare to the original:

for (let i = 0; i < 3; i++) {
    setTimeout(function() { console.log(i) }, 100);
}
console.log(`'i' outside virtual block: ${i}.`);

You may also want to have a look at this SO answer.

(*) Note that in principle (ie. for other languages), the establishment of the lexical context at this position is no necessity: it all depends on the language semantics from the specs and their embodiment in compilers/interpreters and the runtime.

collapsar
  • 17,010
  • 4
  • 35
  • 61
0

In a for loop, Variables that are declared with the let keyword are local to that specific statement. This is in contrast to variables that are declared with var which ARE NOT local to the loop and are actually set in the same scope as the loop itself and not its inside.

Zejj
  • 66
  • 1
  • 5
  • Are you saying if the `var` is declared in the initialization of the loop, it is hoisted to the same scope (global in this case) as the `for` loop? –  Jun 30 '22 at 12:33
  • that's correct. When using var to declare a variable in a for loop, that variable (the variable i in your case) is in the same scope as the loop (the global scope in your example), which is in contrast to using 'let' in which case, the variable declared is in the scope INSIDE the loop, and not AS the loop (like var does) – Zejj Jun 30 '22 at 12:41