ES6是否为对象属性引入了明确定义的枚举顺序?
var o = {
'1': 1,
'a': 2,
'b': 3
}
Object.keys(o); // ["1", "a", "b"] - is this ordering guaranteed by ES6?
for(let k in o) {
console.log(k);
} // 1 2 3 - is this ordering guaranteed by ES6?
答案 0 :(得分:58)
对于for-in
和Object.keys
:否。
对于其他一些操作:是,通常。
虽然ES6 / ES2015添加了属性顺序,但由于旧版兼容性问题,它不需要for-in
或Object.keys
遵循该顺序。
for-in
循环根据[[Enumerate]]进行迭代,[定义为(强调我的):
当调用 O 的[[Enumerate]]内部方法时,如下所示 采取的步骤:
返回下一个方法迭代的Iterator对象(25.1.1.2) 覆盖 O 的可枚举属性的所有字符串值键。该 Iterator对象必须从%IteratorPrototype%(25.1.2)继承。 枚举属性的机制和顺序不是 指定但必须符合 [1] 。
下面指定的规则
ES7 / ES2016删除[[Enumerate]]内部方法,而是使用抽象操作EnumerateObjectProperties,但就像[[Enumerate]]一样,它没有指定任何顺序。
另见Object.keys
的引用:
如果实现定义了枚举的特定顺序 for-in声明,[...]
这意味着实现不需要定义特定的枚举顺序。这个has been confirmed由ECMAScript 2015语言规范的项目编辑Allen Wirfs-Brock在规范完成后发布的帖子中发布。{/ p>
其他操作,例如Object.getOwnPropertyNames
,Object.getOwnPropertySymbols
和Reflect.ownKeys
,按照以下顺序执行普通对象:
此行为在[[OwnPropertyKeys]]内部方法中定义。但请注意exotic objects可能会以不同方式定义该方法,例如
console.log(Reflect.ownKeys(new Proxy({}, {
ownKeys: () => ['3','1','2']
}))); // ['3','1','2'], the integer indices are not sorted!
[1] 下面说:
[[Enumerate]]必须获取目标对象的属性键 通过调用[[OwnPropertyKeys]]内部方法,好像一样。
[[OwnPropertyKeys]]的顺序是明确定义的。但是不要让那些让你感到困惑:“好像”只意味着“相同的属性”,而不是“相同的顺序”。
这可以在EnumerableOwnNames中看到,它使用[[OwnPropertyKeys]]获取属性,然后命令它们
与迭代器产生的相对顺序相同 如果调用了[[Enumerate]]内部方法,则会返回
如果[[Enumerate]]需要以[[OwnPropertyKeys]]的相同顺序进行迭代,则不需要重新排序。
答案 1 :(得分:6)
此问题与EcmaScript 2015(ES6)有关。但应注意,EcmaScript2017规范中有removed以下是先前出现在Object.keys
规范中的以下段落,此处引自the EcmaScript 2016 specification:
如果实现为for-in语句定义了枚举的特定顺序,则必须对步骤3中返回的数组元素使用相同的顺序。
此外,EcmaScript 2020规范removes EnumerableOwnPropertyNames
部分的以下段落,仍显示为in the EcmaScript 2019 specification:
- 对 properties 的元素进行排序,以便它们与Iterator产生的相对顺序相同,如果用 O 调用EnumerateObjectProperties内部方法,则该迭代器将返回这些相对顺序。
这些删除操作意味着从EcmaScript 2020起,Object.keys
强制执行与Object.getOwnPropertyNames
和Reflect.ownKeys
相同的特定顺序,即OrdinaryOwnPropertyKeys中指定的顺序。顺序是:
1 array index是一个字符串值的属性键,它是规范数字String 2 ,其数值 i 为范围+0≤ i <2 32 -1的整数。
2 canonical numeric String是ToString
或字符串“ -0”将产生的数字的字符串表示形式。例如,“ 012”不是规范的数字字符串,而是“ 12”。
应该注意,所有主要实现都已经在几年前与该命令保持一致。
答案 2 :(得分:4)
如其他答案所述,ES2015并未为(非常常用的)属性迭代方法for-in
,Object.keys
和JSON.stringify
定义枚举顺序,而 为其他方法(如Reflect.ownKeys
)定义了枚举方法。 但是,这种不一致很快将不再存在,并且所有属性迭代方法将以可预测的方式进行迭代。
正如许多人在使用JS和注释中所观察到的那样,尽管上述方法的规范并未保证属性迭代的顺序,但是每个实现几乎总是以相同的确定性顺序进行迭代。结果,有一个(完成的)提案可以更改规范以使此行为正式生效:
Specifying for-in enumeration order(Stage 4)
使用此建议,在大多数情况下,保证for..in
,Object.keys
/ values
/ entries
和JSON.stringify
依次迭代:< / p>
(1)数值数组键
(2)个非符号键,按插入顺序
(3)符号键,按插入顺序
与Reflect.ownKeys
和其他已保证以这种方式迭代的方法的顺序相同。
specification text很简单:EnumerateObjectProperties
,由for..in
等调用的有问题的抽象方法,其顺序使用未指定,{{3 }}调用will now,这是为其指定迭代顺序 的内部方法。
目前有几种奇怪的情况,实现尚无法达成共识,在这种情况下,产生的顺序将[[OwnPropertyKeys]]
,但这种情况很少。