Skip to content Skip to sidebar Skip to footer

What's The Action Scope Of For-loop In Es6?

What's exactly the action scope of let in a for-loop in JavaScript? The external console.log throws an error: 'Uncaught Reference Error: i is not defined' It proves i is in a

Solution 1:

The body of a for loop (with a let variable declaration) has two scopes (or LexicalEnvironments): one scope is the iteration environment, which contains the variables declared with let in the for loop declaration, and the inner scope contains variables declared inside the for loop body (after the {). This is described in the specification, starting at 13.7.4.7 Runtime Semantics: LabelledEvaluation

IterationStatement : for ( LexicalDeclaration Expression; Expression ) Statement

(this is what a for loop which declares a variable with let is.)

Evaluating the above eventually gets you to:

  1. Let bodyResult be ForBodyEvaluation(the first Expression, the second Expression, Statement, perIterationLets, labelSet).

Note that "Statement" can be a block (can start with { and end with }, as most for loop bodies do) - this is very important, because a block creates another lexical environment.

13.7.4.8 Runtime Semantics: ForBodyEvaluation says:

  1. Perform ? CreatePerIterationEnvironment(perIterationBindings).

  2. Repeat,

    b. Let result be the result of evaluating stmt.

    ...

    e. Perform ? CreatePerIterationEnvironment(perIterationBindings).

where CreatePerIterationEnvironmentcreates an environment containing the variables declared with let in the for loop declaration:

    g. For each element bn of perIterationBindings, do

        i. Perform ! thisIterationEnvRec.CreateMutableBinding(bn, false).

        ii. Let lastValue be ? lastIterationEnvRec.GetBindingValue(bn, true).

        iii. Perform thisIterationEnvRec.InitializeBinding(bn, lastValue).

So, there are two scopes: one created by CreatePerIterationEnvironment, and one created by the block that the stmt is.

for (let i = 0; i < 3; i++) {
  let foo = 'f';
}

Here, i is contained in the outer iteration environment, and foo is contained in the inner block, which is a different environment. If you made those two variable names names the same, no error is thrown because let <variableName> inside the block creates a variable scoped to that block, and does not try to overwrite the variable with the same name in the iteration environment.

Solution 2:

ECMAScript 2015 has a particular clause to cover what a for loop does when it has a variable defined with let in the control structure.

My reading of it is that it is specifically designed to provide a separate environment record for the variable within the loop body so that its value in a loop iteration will be seen by nested functions used as call backs - and in the process obviating the need to capture the value of a loop variable in a closure.

Essentially

for( let i=0; condition; ++i) {
    ...
}
  • creates an environment record for the loop body with a binding for i set to zero;
  • executes the loop body
  • creates a new environment record for the next iteration, increments i and stores the result in the new environment record for the next loop iteration (if it executes).

Comparing the behavior with block scoping of variables within curly braces is misleading - the lexical scoping of the variable to the for loop body does not require block braces to be used:

for( let i = 0;  i < 2; ++i) continue;
console.log( i);

So the body scoping of the let variable is a language design "special".

Refer to CertainPerformance's answer for technical details of the specification.

Solution 3:

Variables declared with let are not visible (accessible) outside their scope, which explains the error raised by the console.log that is outside the for loop).

It also explains why the loop runs three times and prints 4, the explanation is that the i inside the for loop block (the one declared as let i = 4;) is different than the i in the loop header due to the invisibility that comes with the let keyword, i.e., the i in let i = 4; which is inside the for loop block is not visible in the for (let i = 0; ...), they're different, and so the one inside the block is not affecting the one outside (in the header).

Post a Comment for "What's The Action Scope Of For-loop In Es6?"