for,for-in和for-of循环中变量的范围规则不一致

时间:2018-03-30 11:04:30

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

所以我注意到我必须在for循环中使用const,并且不能使用const。但是,我发现我可以在for-infor-of结构(下面的代码)中使用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); } 循环以不同的方式实现/更原始,而其他构造desugar到for循环,其中迭代变量在for循环的顶部分配。

let

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

  

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

这似乎也是错误的,因为如果我们使用i i,则for循环后for (let i = 0; i < 3; i++) { console.log(i); } // Doesn't work as expected console.log(i); 不再在{{1}}循环中(与其他语言一致) )

{{1}}

我的问题是这个行为是否在某个地方的规范中是预期和定义的? MDN对此没有太多说明。

4 个答案:

答案 0 :(得分:3)

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

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

现在你在for循环中所做的是递增&#34; i&#34;,它被定义为常量。

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

答案 1 :(得分:2)

根据spec

  

ForDeclaration:LetOrConst ForBinding

for-in for-of 语句允许

letconst

另外,根据spec中提到的运行时语义

  

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

此表达式for (const v in object3) { 为每次迭代执行并提供新的绑定

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

答案 2 :(得分:1)

你的第一个问题已由@NullDev回答,所以我要去第二个问题:

  

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

“这些变量不是循环的局部变量”是指var关键字创建的计数器。如果使用let,则计数器的范围仅在for循环中。这是另一个预期的行为,因为var具有无广泛的范围。是的,文档有点含糊不清。

答案 3 :(得分:1)

  

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

没有。你可以在const循环中使用for声明就好了。问题只是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循环以不同的方式实现/更原始,而其他构造desugar到for循环,其中迭代变量在for循环的顶部分配。

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

  • for ([var] init; condition; update) {
        body
    }
    [var] init;
    while (condition) {
        body;
        update;
    }
  • for (const init; condition; update) {
        body
    }

    变为

    {
        const init;
        while (condition) {
            body;
            update;
        }
    }
  • for (let init; condition; update) {
        body
    }

    变为something more complicated

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

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

    变为

    {
        const _iterator = iterable[Symbol.iterator]();
        let _result;
        while (!(_result = _iterator.next()).done) {
            [var]/let/const target = _result.value;
            body;
        }
    }
  • for (… in enumerable)for (… of Reflect.enumerate(enumerable))相同。

  

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

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