为什么 'for (a of a)' 可以正确地迭代一个数组?

11

考虑以下代码片段

var a = [1, 2, 3, 4];
for (a of a) { // The first 'a' is made by mistake
    console.log(a);
}

for 循环中,第一个 a 是因为错误而被写入的。我认为上述代码应该会出现错误,因为在第一次迭代中将 a 赋值为 1 后,再次迭代时 a 不是一个可迭代对象,因此应该抛出错误。

实际上,结果如下:

1
2
3
4

看起来上面的代码可以正确遍历数组。在 for 循环之后,a 的结果是 4为什么呢?

> a
4

为了进一步研究,我尝试从ECMA-6文档中找到一些信息,但是我被以下语句所困惑。

for ( var ForBinding of AssignmentExpression ) Statement

for ( ForDeclaration of AssignmentExpression ) Statement

为了理解ForBindingForDeclaration,请测试以下代码。

var a = [1, 2, 3, 4];
for (var a of a) {
    console.log(a);
}
console.log(a);

很不幸,结果与之前的代码相同。在 for (var a in a)for (a in a) 之间有什么区别?

1个回答

9
for评估“AssignmentExpression”的值并对其进行迭代。该值仅在迭代开始时获取一次,因此重复使用同一变量是完全有效的(也非常令人困惑)。
存在varfor (a of ...)for (var a of ...)在代码中没有任何区别,因为您已经定义了a - 因此它只会重新声明相同的变量。
要完全准确,有些情况下行为是不同的-当当前函数的外部范围中声明a时,var版本将掩盖该值(因为在JavaScript中,所有var语句都提升到函数范围的顶部)。
var a = [1,2,3];
function tryForVar()   {
   // Note that declaration of `a` is hoisted here as var a = undefined;
   // for (var a ... does not work as expected as local 'a' is undefined
   for (var a of a) { 
     console.log(a); // log 'undefined' once
   }
   console.log(a); // undefined 
} 
tryForVar();
console.log(a); // [1,2,3]

function tryFor()   {
   // Note that declaration of `a` from outer scope
   // for (a ... works fine as it uses outer 'a'
   for (a of a) { 
     console.log(a); // logs all 1,2,3 in sequence
   }
   console.log(a); // 3
} 
tryFor();
console.log(a); // 3

这似乎是唯一一个真正回答了问题并且也是唯一正确的答案。不确定为什么其他人会给它一个负评。我给它加1分。 - jfriend00
6
我没有投反对票,但使用 var 声明的变量不会被重新声明。在其作用域中,声明会被提升,且代码中稍后遇到的 var 会被 _忽略_。 - Teemu
1
@Teemu 是的,我的陈述确实过于简化了,应该是“如果你使用 var a 版本,那么循环前后 a 的含义仍然相同,但由于变量提升可能会遮蔽外部作用域的变量(因此完全改变你正在迭代的内容)”。 - Alexei Levenkov
我不确定这是否正确。在迭代开始时,AssignmentExpression并非只读取一次。您可以在for..of循环期间更改数组中的值以查看情况并非如此。 - JayChase
@JayChase OP所展示的情况中,表达式的值是数组本身,而不是数组的副本。因此,您仍然可以更改数组的内容(只要您有其他引用)。也许想象一个临时变量来存储表达式结果是一种可视化发生的情况的方法。请注意,在迭代过程中更改数组/对象时会发生什么是大多数人难以预测的-请尽量避免这种情况。 - Alexei Levenkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接