问题 为什么'for(a of a)'能正确迭代数组?


请考虑以下代码段

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

首先 a 在里面 for 循环是错误写的。我认为上面的代码应该运行错误,因为什么时候 a 分配给 1 然后在第一次迭代中 a 不是 可迭代的对象。所以在下一次迭代中应该抛出一个错误。

实际上,结果如下:

1
2
3
4

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

> a
4

为了进一步调查,我试图从中找到一些信息 ECMA-6 doc,但我对以下声明感到困惑。

for(var ForBinding of AssignmentExpression)语句

for(ForDeccration of AssignmentExpression)声明

要理解 ForBinding 和 ForDeclaration,测试以下代码。

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)


3452
2018-01-10 09:01


起源



答案:


for 评估“AssignmentExpression”的值并迭代它。该值仅在迭代开始时获得一次,因此重用相同的变量是完全有效的(也非常令人困惑)。

存在 varfor (a of ...) 和 for (var a of ...) 没有做任何 1 你已经拥有的代码差异 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

9
2018-01-10 09:05



这似乎是实际解决问题的唯一答案,似乎也是唯一正确的答案。不知道为什么别人给它一个downvote。来自我的+1。 - jfriend00
我没有投票,但变量声明了 var 没有重新申报。宣言在其范围内悬挂,并且 var以后在代码中遇到了 忽视。 - Teemu
@Teemu是的,我的陈述确实过于简单化,而应该是“如果你使用 var a 版本的含义 a 循环之前和之后仍然是相同的,但它可能会因为提升而影响外部范围的变量(因此完全改变你所迭代的内容)“。 - Alexei Levenkov
我不确定这是对的。 AssignmentExpression在迭代开始时不会只读取一次。您可以在for ...循环期间更改数组中的值,以查看情况并非如此。 - JayChase
在OP所示的情况下,表达式的@JayChase值是数组本身 - 它不是a 数组的副本。因此,您仍然可以更改数组的内容(只要您有其他引用)。也许想象用于存储表达式结果的临时变量是一种可视化正在发生的事情的方法。请注意,代码在迭代时更改数组/对象时发生的情况对于大多数人来说很难预测 - 尽量避免它。 - Alexei Levenkov


答案:


for 评估“AssignmentExpression”的值并迭代它。该值仅在迭代开始时获得一次,因此重用相同的变量是完全有效的(也非常令人困惑)。

存在 varfor (a of ...) 和 for (var a of ...) 没有做任何 1 你已经拥有的代码差异 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

9
2018-01-10 09:05



这似乎是实际解决问题的唯一答案,似乎也是唯一正确的答案。不知道为什么别人给它一个downvote。来自我的+1。 - jfriend00
我没有投票,但变量声明了 var 没有重新申报。宣言在其范围内悬挂,并且 var以后在代码中遇到了 忽视。 - Teemu
@Teemu是的,我的陈述确实过于简单化,而应该是“如果你使用 var a 版本的含义 a 循环之前和之后仍然是相同的,但它可能会因为提升而影响外部范围的变量(因此完全改变你所迭代的内容)“。 - Alexei Levenkov
我不确定这是对的。 AssignmentExpression在迭代开始时不会只读取一次。您可以在for ...循环期间更改数组中的值,以查看情况并非如此。 - JayChase
在OP所示的情况下,表达式的@JayChase值是数组本身 - 它不是a 数组的副本。因此,您仍然可以更改数组的内容(只要您有其他引用)。也许想象用于存储表达式结果的临时变量是一种可视化正在发生的事情的方法。请注意,代码在迭代时更改数组/对象时发生的情况对于大多数人来说很难预测 - 尽量避免它。 - Alexei Levenkov