【问题标题】:Inconsistent scope rules of variables in for, for-in and for-of loopsfor、for-in 和 for-of 循​​环中变量的范围规则不一致
【发布时间】:2018-09-09 10:11:06
【问题描述】:

所以我注意到我必须在for 循环中使用let,而不能使用const。但是,我发现我可以在for-infor-of 构造中使用const(代码如下)。直觉上我可以合理地解释这是因为 for 循环的实现方式不同/更原始,而其他构造去糖化到 for 循环中,其中迭代变量被分配在 for 循环的顶部。

// Doesn't work
for (const i = 0; i < 3; i++) {
  console.log(i);
}

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

// Works
const object2 = ['a', 'b', 'c'];
for (const v of object2) {
  console.log(v);
}

// Works
const object3 = {
  a: 'a',
  b: 'b',
  c: 'c',
};
for (const v in object3) {
  console.log(v);
}

我在 Mozilla MDN 上唯一能找到的就是 for 循环页面:

此表达式可以选择使用 var 声明新变量 关键词。这些变量不是循环本地的,即它们在 for 循环所在的范围相同。这个表达式的结果是 丢弃。

这似乎也是错误的,因为如果我们将let 用于i,那么ifor 循环之后不再在范围内(这与其他语言一致)

for (let i = 0; i < 3; i++) {
  console.log(i);
}
// Doesn't work as expected
console.log(i);

我的问题是这种行为是否是预期的并在规范中的某处定义? MDN 对此并没有多说。

【问题讨论】:

  • 关于“...if we use a let for i...”,这与使用 var 而不是 的 MDN 代码不同让.
  • @robG 啊,真的,那句话的第二句话是指第一句话。我的立场是正确的。

标签: javascript for-loop ecmascript-6 scope for-in-loop


【解决方案1】:

是的。这确实是预期的行为。

const 定义了一个变量,顾名思义,该变量保持不变。这意味着 const 的值不能改变。

现在您在for 循环中所做的就是递增“i”,它被定义为一个常量。

for (const i = 0; i < 3; i++ /* <- this doesn't work */ ) {
    console.log(i);
}

但是,使用for .. infor .. of,您只需绑定变量。

换句话说:使用for .. in/off,变量在循环执行之前被分配一次,而不是每次迭代。所以const确实可以用。

至于参考:

ForDeclaration : LetOrConst ForBinding

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-for-in-and-for-of-statements-static-semantics-boundnames

【讨论】:

    【解决方案2】:

    根据spec

    ForDeclaration : LetOrConst ForBinding

    letconst 允许在 for-infor-of 语句中使用。

    另外,根据spec

    中提到的运行时语义

    对于ForBinding的BoundNames的每个元素名做

    这个表达式for (const v in object3) {在每次迭代中执行并提供一个新的绑定

    但是,使用 简单的 for 循环 - for (const i = 0; i &lt; 3; i++) {const i 只执行一次因此它不允许您重新为其赋值

    【讨论】:

      【解决方案3】:

      @NullDev 已经回答了您的第一个问题,所以我要回答第二个问题:

      此表达式可以选择使用 var 声明新变量 关键词。这些变量不是循环的本地变量,即它们在 for 循环所在的范围相同。这个表达式的结果是 丢弃。

      “这些变量不是循环本地的”是指由var 关键字创建的计数器。如果您使用let,则计数器的范围仅在该 for 循环中。这是另一个预期的行为,因为 var 的范围很广。是的,文档有点模棱两可。

      【讨论】:

        【解决方案4】:

        所以我注意到我必须在 for 循环中使用 let,而不能使用 const。

        没有。您可以在 for 循环中使用 const 声明就好了。问题只是const 声明了一个常量绑定,因此增量i++const i 不起作用(它应该抛出异常,确保您处于严格模式)。

        const的使用示例:

        for (const o = {index: 0, value: null}; o.index < arr.length; o.index++) {
            o.value = arr[o.index];
            doSomething(o);
        }
        

        或者一个更有意义的地方:

        for (const iterator = makeIterator(); !iterator.isDone(); iterator.next())
            doSomething(iterator.getCurrent());
        }
        

        直觉上我可以解释这是因为 for 循环的实现方式不同/更原始,而其他构造去糖化到 for 循环中,其中迭代变量被分配在 for 循环的顶部。

        是的。在for 循环中,您需要自己更新迭代变量。

        • for ([var] init; condition; update) {
              身体
          }
          变成
          [var] init;
          而(条件){
              身体;
              更新;
          }
        • for (const init; condition; update) {
              身体
          }

          变成

          { 常量 init; 而(条件){ 身体; 更新; } }
        • for (let init; condition; update) {
              身体
          }

          变成something more complicated

        for … infor … of 循环中,您只需为生成的值声明一个赋值目标表达式。

        • for ([var]/let/const target of iterable ) {
              身体
          }

          变成

          { const _iterator = 可迭代[Symbol.iterator](); 让_结果; 而 (!(_result = _iterator.next()).done) { [var]/let/const target = _result.value; 身体; } }
        • for (… in enumerable)for (… of Reflect.enumerate(enumerable)) 相同。

        我在 Mozilla MDN 上唯一能找到的就是 for 循环页面,这似乎也是错误的。

        是的,看起来该部分尚未针对 ES6 进行更新。

        【讨论】:

          猜你喜欢
          • 2018-04-25
          • 2018-11-14
          • 1970-01-01
          • 1970-01-01
          • 2017-07-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多