一尘不染

JavaScript``let''的说明和for循环的块作用域

javascript

我知道这let可以防止重复声明,这很好。

let x;
let x; // error!

用声明的变量let也可以在可以预期的闭包中使用

let i = 100;
setTimeout(function () { console.log(i) }, i); // '100' after 100 ms

我有点难以掌握的是如何let应用于循环。这似乎是特定于for循环的。考虑经典问题:

// prints '10' 10 times
for (var i = 0; i < 10; i++) { process.nextTick(_ => console.log(i)) }
// prints '0' through '9'
for (let i = 0; i < 10; i++) { process.nextTick(_ => console.log(i)) }

为什么let在这种情况下使用有效?在我的想象中,即使只有一个块可见,for实际上为每次迭代创建了一个单独的块,并且let声明是在该块内部完成的,但是只有一个let声明可以初始化该值。这只是ES6的语法糖吗?这如何运作?

我了解和之间的区别varlet并在上面进行了说明。我特别想了解为什么不同的声明使用for循环会导致不同的输出。


阅读 523

收藏
2020-04-25

共1个答案

一尘不染

这只是ES6的语法糖吗?

不,它不仅仅是语法糖。详细资料埋在§13.6.3.9中 CreatePerIterationEnvironment

这如何运作?

如果letfor语句中使用该关键字,它将检查其绑定的名称,然后

  • 使用以下名称创建一个新的词法环境:a)初始化程序表达式b)每次迭代(以前是评估增量表达式)
  • 将具有这些名称的所有变量的值从一个复制到下一个环境

您的循环语句for (var i = 0; i < 10; i++) process.nextTick(_ =>console.log(i));简化为一个简单的

// omitting braces when they don't introduce a block
var i;
i = 0;
if (i < 10)
    process.nextTick(_ => console.log(i))
    i++;
    if (i < 10)
        process.nextTick(_ => console.log(i))
        i++;
        …

while for (let i = 0; i < 10; i++) process.nextTick(_ => console.log(i));
does “desugar” to the much more complicated

// using braces to explicitly denote block scopes,
// using indentation for control flow
{ let i;
  i = 0;
  __status = {i};
}
{ let {i} = __status;
  if (i < 10)
      process.nextTick(_ => console.log(i))
      __status = {i};
}   { let {i} = __status;
      i++;
      if (i < 10)
          process.nextTick(_ => console.log(i))
          __status = {i};
    }   { let {i} = __status;
          i++;
          …
2020-04-25