ECMAScript 2015:for 循环中的常量

IT技术 javascript for-loop scope constants ecmascript-6
2021-02-04 08:37:33

下面的两个(或两个都不是)代码片段中的哪一个应该在完整的 ECMAScript 2015 实现中工作:

for (const e of a)

for (const i = 0; i < a.length; i += 1)

根据我的理解,第一个示例应该可以工作,因为e每次迭代都会进行初始化。i在第二个版本中不应该也是这种情况吗?

我很困惑,因为现有的实现(Babel、IE、Firefox、Chrome、ESLint)似乎不一致并且具有 的完整实现const,以及两个循环变体的各种行为;我也无法在标准中找到具体的点,因此将不胜感激。

3个回答

以下 for-of 循​​环有效:

for (const e of a)

ES6 规范将其描述为:

ForDeclaration : LetOrConst ForBinding

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

强制循环将不起作用:

for (const i = 0; i < a.length; i += 1)

这是因为在执行循环体之前,声明只被评估一次。

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-for-statement-runtime-semantics-labelledevaluation

为什么要链接到草案而不是最终规范?ecma-international.org/ecma-262/6.0/index.html您还引用了错误的for循环评估规则const ...不是表达式。您需要查看 的规则for ( LexicalDeclaration Expression ; Expression) Statement
2021-03-14 08:37:33
@FelixKling 仍然为旧链接添加了书签。你是对的评估规则。结论应该仍然相同,因为不可变绑定只创建了一次(在第 5 步中)?
2021-03-17 08:37:33
MDN 文档还说“如果您不在块内重新分配变量,您也可以使用 const 而不是 let。” developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-03-18 08:37:33
for (const e of a)在最新版本的 Firefox 中似乎不起作用。我越来越SyntaxError: invalid for/in left-hand side
2021-03-22 08:37:33
是的,但我认为线索在第 9 步。const每次迭代都不会重新声明 s。
2021-04-05 08:37:33

这次我不会引用规范,因为我认为通过示例更容易理解发生了什么。

for (const e of a) …

基本上相当于

{
    const __it = a[Symbol.iterator]();
    let __res;
    while ((__res = __it.next()) && !__res.done) {
        const e = __res.value;
        …
    }
}

为简单起见,我忽略ea表达式的 TDZ ,以及在循环过早退出(在主体中)的情况下的各种__it.return()/__it.throw(e)调用breakthrow

for (const i = 0; i < a.length; i += 1) …

基本上相当于

{
    const i = 0;
    while (i < a.length) {
        …
        i += 1;
    }
}

与 相比let循环中const声明for不会在每次循环迭代中重新声明(并且初始化程序无论如何都不会重新执行)。除非你break在第一次迭代中,否则你i +=会抛出这里。

对我来说(节点 5.0.0)for-of 循​​环没有按预期工作。在 'let a = [1,3,5], b = []' 和 'for (const e of a) { b.push(e) }' 之后我得到 'b == [ 1, 1, 1 ]' . 节点错误还是预期行为?根据您的代码,我希望每次迭代都有一个新的“const e = __res.value”分配。
2021-03-26 08:37:33
我知道这是旧的,但稍微更正:while ( ( __res = __it.next() ) && !__res.done) {否则__res最终是truefalse
2021-03-27 08:37:33
@JürgenStrobel:一个旧的节点错误,它const具有类似var的范围并且不会引发分配。使用严格模式。
2021-03-31 08:37:33
@JDB 谢谢,已修复!顺便说一句,如果你刚刚编辑它,我会很好。
2021-04-05 08:37:33

您的第二个示例绝对不应该工作,因为i声明一次而不是在每次迭代时,这只是该类循环如何工作的函数。

您可以在常规浏览器中尝试此操作:

for (var i = 0, otherVar = ""; i < [1,2,3,4].length; i += 1){
  console.log(otherVar)
  otherVar = "If otherVar was initialized on each iteration, then you would never read me.";
}

constfor循环中并不是完全不允许的情况只有for那会修改 const 才是。

这些是有效的:

for(const i = 0;;){ break } 
for(const i = 0; i < 10;){ break; } 

这些是无效的:

for(const i = 0;;){ ++i; break; } 
for(const i = 0;;++i){ if(i > 0) break; }

我不知道为什么 Firefox 在阅读 ES2015 规范后会给出一个 SyntaxError(虽然我确信 Mozilla 的聪明人是正确的),它似乎应该引发一个异常:

在环境记录中创建一个新的但未初始化的不可变绑定。字符串值 N 是绑定名称的文本。如果 S 为真,则尝试在初始化之前访问绑定的值或在初始化之后设置它总是会抛出异常,无论引用该绑定的操作的严格模式设置如何。S 是一个可选参数,默认为 false。

@FelixKling 您认为需要引用哪个语句?const不允许自己被重新分配(这没有争议),正如我的例子清楚地展示了for与他隐含的期望相反的变量定义部分是如何工作的。的值let可以更改,它在功能上等同var于给出的示例,但let不是问题的一部分。
2021-03-17 08:37:33
不说出处怎么能这么说呢?为什么会是这种情况 forconst而不是 for let
2021-03-19 08:37:33
在聊天中评论:如果您使用let,那么每次迭代都会获得自己的i. for(let foo = 0; i < 10; ++i){} 相当于 (funtion() { for(var i = 0; i < 10; ++i){ (function(i) { }(i)) } }()); This is where I come from:let并且const都是块范围的。for/infor/of公开相同的行为constlet,但正常的for循环没有。明确地const区别对待(可以理解,也许)。您只是说它是“声明一次”,但这在 IMO 中过于简化了。
2021-04-03 08:37:33
所以你明确的意思const i是只声明一次,但let i不会?您的示例仅演示如何var i工作,而不是const i. 由于var,const之间有明显的区别let,我认为引用他关于 的规范const将非常有value。
2021-04-05 08:37:33
@FelixKling 你误读了我正在输入的内容。我是说for 循环那部分中的所有内容都声明了一次。然后正交const值只能分配一次,任何重新声明的尝试const都不起作用。我的例子是为了证明声明只发生一次的事实,提出问题的人理解const. let不是问题,不,我没有明确表示您的建议,您误读了。
2021-04-08 08:37:33