ECMAScript 2015:for 循环中的 const
Posted
技术标签:
【中文标题】ECMAScript 2015:for 循环中的 const【英文标题】:ECMAScript 2015: const in for loops 【发布时间】:2015-11-06 09:01:19 【问题描述】:以下两个(或两者都不/两者)代码片段中的哪一个应该在完整的 ECMAScript 2015 实现中工作:
for (const e of a)
for (const i = 0; i < a.length; i += 1)
根据我的理解,第一个示例应该可以工作,因为每次迭代都会初始化 e
。第二个版本的i
不应该也是这样吗?
我很困惑,因为现有的实现(Babel、IE、Firefox、Chrome、ESLint)似乎不一致,并且具有const
的完整实现,具有两个循环变体的各种行为;我也无法在标准中找到具体点,因此将不胜感激。
【问题讨论】:
const 用于常量 x.x,你应该改用 let @JamesThorpe 不,我的问题只是试图弄清楚行为应该是什么。例如,ESLint 认为第一个示例 OK(并且首选prefer-const
选项),而第二个示例无效。大多数浏览器实现都认为这两个示例都无效。
AFAIK 第一个没问题,因为每次迭代都会重新初始化它。它适用于 Chrome。
@lyschoening 这也不应该适用于第二个例子吗?
@adrianp 绝对不是第二个例子。常规的 for 循环本质上等同于 const i = 0; while(i < a.length) /* for body */ i += 1
【参考方案1】:
以下 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 循环不起作用:
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
的规则。
@FelixKling 仍然为旧链接添加了书签。您对评估规则是正确的。结论应该仍然相同,因为不可变绑定只创建一次(在步骤 5 中)?
对,但我认为线索在第 9 步。const
s 不会在每次迭代时重新声明。
for (const e of a)
似乎无法在最新版本的 Firefox 中使用。我收到SyntaxError: invalid for/in left-hand side
MDN 文档还说“如果您不重新分配块内的变量,您也可以使用 const 而不是 let。” developer.mozilla.org/en-US/docs/Web/javascript/Reference/…【参考方案2】:
这次我不会引用规范,因为我认为通过示例更容易理解发生了什么。
for (const e of a) …
基本上相当于
const __it = a[Symbol.iterator]();
let __res;
while ((__res = __it.next()) && !__res.done)
const e = __res.value;
…
为简单起见,我忽略了a
表达式有一个带有e
的TDZ,并且在循环过早退出的情况下会调用各种__it.return()
/__it.throw(e)
(break
或throw
在体内)。
for (const i = 0; i < a.length; i += 1) …
基本上等价于
const i = 0;
while (i < a.length)
…
i += 1;
In contrast to let
,const
循环中的 const
声明不会在每次循环迭代中重新声明(并且无论如何都不会重新执行初始化程序)。除非你在第一次迭代中 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' 分配。 @JürgenStrobel:一个旧的节点错误,其中const
具有类似var
的范围并且不会引发分配。使用严格模式。
我知道这是旧的,但稍作更正:while ( ( __res = __it.next() ) && !__res.done)
。否则__res
最终成为true
或false
@JDB 谢谢,已修复!顺便说一句,如果您刚刚编辑它,我会很好。【参考方案3】:
您的第二个示例绝对不应该工作,因为 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.";
const
在for
循环中并不是完全不允许的。只有 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。
【讨论】:
不注明出处怎么能这么说呢?为什么const
会出现这种情况,而let
不会出现这种情况?
@FelixKling 您认为哪个陈述需要引用? const
不允许重新分配自己(这没有争议),正如我的示例清楚地展示了 for
如何与他的隐含期望相反地用于变量定义部分。 let
的值可以更改,它在功能上等同于给定示例中的var
,但let
不是问题的一部分。
所以您明确表示const i
只声明一次,但let i
不会?您的示例仅演示了 var i
的工作原理,而不是 const i
。由于var
、const
和let
之间存在明显区别,我认为引用他关于const
的规范将非常有价值。
@FelixKling 你误读了我输入的内容。我是说 for 循环的那个部分中的 everything 被声明一次。然后正交const
值只能分配一次,任何重新声明const
的尝试都将不起作用。我的例子是为了证明声明只发生一次,提问的人理解const
的语义。 let
不是问题,不,我没有明确表示您的建议,您误读了。
在聊天中评论:如果您使用let
,那么每次迭代都会获得自己的i
副本。 for(let foo = 0; i < 10; ++i)
等同于 (funtion() for(var i = 0; i < 10; ++i) (function(i) (i)) ());
这就是我的来源:let
和 const
都是块作用域。 for/in
和 for/of
为 const
和 let
提供相同的行为,但正常的 for
循环不会。它明确以不同的方式对待const
(可以理解也许)。您只是说它是“声明一次”,但这过于简化了 IMO。以上是关于ECMAScript 2015:for 循环中的 const的主要内容,如果未能解决你的问题,请参考以下文章