ECMAScript 2015: const in for 循环

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

for (const e of a)

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

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

我感到困惑是因为现有的实现(Babel,IE,Firefox,Chrome,ESLint)似乎并不一致,而且有一个完整的 const实现,有两个循环变体的不同行为; 我也无法在标准中找到一个具体的点,所以这将是非常感激的。

54026 次浏览

Your second example should definitely not work because i is declared once and not on each iteration this is just a function of how that category of loops work.

You can try this in a regular browser:

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.";
}

It's not the case that const is entirely disallowed in for loops. Only for that will modify const is.

These are valid:

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

These are invalid:

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

I'm not sure why Firefox gives a SyntaxError after reading the ES2015 spec (although I'm sure the clever folk at Mozilla are correct), it seems like it's supposed to raise an exception:

Create a new but uninitialized immutable binding in an Environment Record. The String value N is the text of the bound name. If S is true then attempts to access the value of the binding before it is initialized or set it after it has been initialized will always throw an exception, regardless of the strict mode setting of operations that reference that binding. S is an optional parameter that defaults to false.

The following for-of loop works:

for (const e of a)

The ES6 specification describes this as:

ForDeclaration : LetOrConst ForBinding

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

The imperative for loop will not work:

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

This is because the declaration is only evaluated once before the loop body is executed.

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

I won't cite the spec this time, because I think it's easier to understand what happens by example.

for (const e of a) …

Is basically equivalent to

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

For simplicity I've ignored that there's a TDZ with e for the a expression, and the various __it.return()/__it.throw(e) calls in the case the loop exits prematurely (break or throw in the body).

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

is basically equivalent to

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

In contrast to let, a const declaration in a for loop does not get redeclared in every loop iteration (and the initialiser is not re-executed anyway). Unless you break in the first iteration, your i += will throw here.