"for...of"循环迭代是否遵循JavaScript中的数组顺序?

Does "for...of" loop iteration follow the array order in JavaScript?

使用 for...in 遍历数组并不能保证顺序,但是 ES6 引入了一个新的结构 for...of

我对 for...of 实现的有限测试表明它确实在数组上按顺序迭代,但这是 属性 保证吗?

My limited testing of implementations of for...of indicate it does iterate in order on array, but is this property guaranteed?

是的。但是寻找它有点复杂,因为 for of 不仅迭代数组(就像 for in 枚举对象)。相反,它通常迭代所有 iterable 对象 - 按照它们各自的迭代器提供的顺序。

事实上,数组就是这样一个可迭代对象,当从中获取迭代器时,它将是一个迭代器,它以与在数组中找到的顺序相同的顺序生成数组的所有元素。您可以阅读 spec for ArrayIterator objects,它们基本上像 for (var index=0; index<array.length; index++) yield array[index]; 循环一样工作。

根据 for..of

的 ES6 规范
for ( LeftHandSideExpression of AssignmentExpression ) Statement

If LeftHandSideExpression is either an ObjectLiteral or an ArrayLiteral and if the lexical token sequence matched by LeftHandSideExpression can be parsed with no tokens left over using AssignmentPattern as the goal symbol then the following rules are not applied. Instead, the Early Error rules for AssignmentPattern are used.

根据此语法规则定义,当 for..of 循环是 ArrayObject[ 时,将按照标记的词法顺序执行。 =23=] 文字。

这是 David Walsh http://davidwalsh.name/es6-generators 的一篇不错的博客 link,他在其中举例说明了 for..of 循环如何使用迭代器工作。

Iterating over an array using for...in doesn't guarantee order, however ES6 introduces a new construct for...of.

My limited testing of implementations of for...of indicates that it does iterate in order on array, but is this property guaranteed?

是的,for-of在数组上的顺序由array iterator definition保证:它将按照数字索引顺序访问数组中的条目(包括不存在的条目,例如稀疏数组中的那些——或者也许应该是稀疏数组中的那些 not :-)):

Live Example on Babel 的 REPL,这里是使用最新浏览器的现场片段:

"use strict";
let a = [];
a[3] = 'd';
a[0] = 'a';
a.foo = "f";
for (let v of a) {
  console.log(v);
}

输出:

a
undefined
undefined
d

(两个undefined在Babel的REPL中显示为空白。)

上面有两点需要注意:

  1. 即使数组有一个可枚举的 属性 foo,它也没有被访问。

  2. 数组是稀疏的,for-of did 访问了不存在的两个条目(索引 1 和 2)。

for-in,然而, 没有保证在 ES2015(又名“ES6”)到 ES2019 中订购;在 ES2020 中,它遵循与 ES2015 中添加的服从机制(Object.getOwnPropertyNames 等)相同的 属性 顺序(有一些注意事项)。考虑这个例子:

"use strict";
var a = [];
a.foo = "f";
a[3] = 'd';
a[0] = 'a';
a.bar = "b";
var key;
for (key in a) {
  console.log(key);
}

在ES2015到ES2019中,可能会输出

0
3
foo
bar

foo
bar
0
3

或其他。但是,从 ES2020 开始,它被指定为 output

0
3
foo
bar

因为它必须首先按数字顺序访问 整数索引 属性(名称为标准数字形式的字符串的属性),然后是 中的其他属性创建顺序(所以,foobar之前)。

(假定 Array.prototypeObject.prototype 上没有可枚举属性(默认情况下没有)。如果有,我们也会看到它们,但未指定哪里。)

如果你想遍历数组的 for-of 是 ES2015 的一个很好的工具,还有其他有用的工具,如 Array#forEachforEach 在稀疏数组上特别方便;它会跳过不存在的条目)。 for-in 很少是一个好的选择。 this other answer.

中有详尽的选项列表