ES6中for循环的作用范围是什么?

时间:2019-08-30 00:57:22

标签: javascript for-loop

JavaScript的for循环中let的作用范围到底是什么?

for (let i = 0; i < 3; i++) {
  let i = 4;
  console.log(i);
}
console.log(i);

外部console.log引发错误:

  

“未捕获的引用错误:我未定义”

证明i在块操作范围内,但是,为什么在for循环中定义的i不会引发任何重复的定义错误?

3 个答案:

答案 0 :(得分:3)

for循环的主体(带有变量let)具有两个作用域(或LexicalEnvironments):一个作用域是迭代环境 em>,它包含在let循环声明中用for声明的变量,并且内部作用域包含在for循环体内(在{之后)声明的变量。规范中对此进行了描述,从13.7.4.7 Runtime Semantics: LabelledEvaluation

开始
  

IterationStatement:用于(LexicalDeclaration表达式; Expression)语句

(这是for循环,它用let声明变量。)

评估以上内容最终会使您:

  
      
  1. 让bodyResult为ForBodyEvaluation(第一个表达式,第二个表达式,语句,perIterationLets,labelSet)。
  2.   

请注意,“声明”可以是一个 block (可以像大多数{循环主体那样,以}开头并以for结尾)-这非常重要,因为一个块会创建另一个词法环境。

13.7.4.8 Runtime Semantics: ForBodyEvaluation说:

  1. 执行? CreatePerIterationEnvironment(perIterationBindings)。

  2. 重复

    b。令result为评估stmt的结果。

    ...

    e。执行? CreatePerIterationEnvironment(perIterationBindings)。

其中CreatePerIterationEnvironment creates an environment包含let循环声明中用for声明的变量:

g。对于perIterationBindings的每个元素bn,执行

i。执行! thisIterationEnvRec.CreateMutableBinding(bn,false)。

ii。让lastValue为? lastIterationEnvRec.GetBindingValue(bn,true)。

iii。执行thisIterationEnvRec.InitializeBinding(bn,lastValue)。

因此,有两个范围:一个由CreatePerIterationEnvironment创建,另一个由stmt是块创建。

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

在这里,i包含在外部迭代环境中,而foo包含在内部块中,后者是一个不同的环境。如果使这两个变量名称相同,则不会引发错误,因为该块内的let <variableName>将创建一个变量作用于该块的,并且不会尝试使用相同的变量覆盖该变量。 迭代环境中的名称。

答案 1 :(得分:2)

ECMAScript 2015具有一个特殊的子句 1 来说明for循环在控制结构中具有用let定义的变量时的作用。

我的理解是,是专门设计的,目的是为循环体内的变量提供单独的环境记录,以便其循环迭代中的值可以被用作调用的嵌套函数看到支持-并且在此过程中无需捕获闭包中循环变量的值。

基本上

for( let i=0; condition; ++i) {
    ...
}
  • 为循环主体创建一个环境记录,并将i的绑定设置为零;
  • 执行循环体
  • 为下一次迭代创建新的环境记录,递增i并将结果存储在新的环境记录中,以进行下一次循环迭代(如果执行)。

将行为与大括号内变量的块作用域进行比较会产生误导-将变量的词法作用域应用于for循环主体不需要使用大括号:

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

因此let变量的主体作用域是一种语言设计的“特殊”。

1 有关规范的技术细节,请参见CertainPerformance's answer

答案 2 :(得分:0)

let声明的变量在它们的作用域之外不可见(可访问),这解释了console.log在for循环之外引起的错误。

它还解释了为什么循环运行三遍并打印4,原因是for循环块中的i(声明为let i = 4;的块)与由于i关键字附带的不可见性,因此循环标头中的let,即,在for循环块内的i中的let i = 4;for (let i = 0; ...),它们是不同的,因此块中的一个不会影响外部(在标题中)。