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?
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?
注:由于ES2020的,喜欢更老的操作for-in
和Object.keys
必须遵守财产秩序。这并没有改变这样一个事实,即对基本程序逻辑使用属性顺序可能不是一个好主意,因为非整数索引属性的顺序取决于属性的创建时间。
ES2015-ES2019 的答案:
对于for-in
, Object.keys
, 和JSON.stringify
:不。
对于其他一些操作:是的,通常。
虽然ES6 / ES2015增加了财产秩序,它不需要for-in
,Object.keys
或者JSON.stringify
遵循这一顺序,由于传统的兼容性问题。
for-in
循环根据[[Enumerate]]进行迭代,其定义为(强调我的):
当O的 [[Enumerate]] 内部方法被调用时,采取以下步骤:
返回一个 Iterator 对象 ( 25.1.1.2 ),其 next 方法迭代O的可枚举属性的所有字符串值键。Iterator 对象必须从 %IteratorPrototype% ( 25.1.2 )继承。枚举属性的机制和顺序没有指定,但必须符合下面指定的规则[1]。
ES7 / ES2016 移除了 [[Enumerate]] 内部方法,而是使用抽象操作EnumerateObjectProperties,但就像 [[Enumerate]] 一样,它没有指定任何顺序。
还可以看到以下引用Object.keys
:
如果实现为 for-in 语句定义了特定的枚举顺序,则 [...]
这意味着实现不需要定义特定的枚举顺序。ECMAScript 2015 语言规范的项目编辑 Allen Wirfs-Brock 在规范完成后发表的一篇文章中证实了这一点。
其他操作,如Object.getOwnPropertyNames
, Object.getOwnPropertySymbols
, Object.defineProperties
, 和Reflect.ownKeys
对普通对象遵循以下顺序:
此行为在[[OwnPropertyKeys]]内部方法中定义。但是某些奇异的对象对内部方法的定义略有不同。例如,代理的ownKeys
陷阱可能以任何顺序返回一个数组:
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]] 来获取属性,然后对它们进行排序
与 Iterator 生成的相对顺序相同,如果调用 [[Enumerate]] 内部方法,则返回该顺序
如果需要 [[Enumerate]] 以与 [[OwnPropertyKeys]] 相同的顺序进行迭代,则不需要重新排序。
作为覆盖对方的回答,ES2015不会为(很常用的)属性来定义枚举顺序迭代方法for-in
,Object.keys
和JSON.stringify
,而它也定义好其他方法,如枚举的方法Reflect.ownKeys
。但是,这种不一致很快将不复存在,所有属性迭代方法都会以可预测的方式进行迭代。
正如许多人在他们自己的 JS 经验和评论中所观察到的那样,尽管上述这些方法的规范不保证属性迭代顺序,但无论如何,每个实现几乎总是以相同的确定性顺序进行迭代。因此,有一个(已完成的)提议来更改规范以使此行为正式化:
这个建议,在大多数情况下,for..in
,Object.keys
/ values
/ entries
,并JSON.stringify
保证迭代,以便在:
(1) 数字数组键
(2) 非符号键,按插入顺序
(3) 符号键,按插入顺序排列
这与Reflect.ownKeys
已经保证以这种方式迭代的其他方法的顺序相同。
的规范的文本是相当简单的:EnumerateObjectProperties
通过调用的问题抽象方法for..in
等,其顺序使用是未指定的,现在将调用[[OwnPropertyKeys]]
,这是它的迭代顺序内部方法被指定。
有一些奇怪的情况目前的实现不同意,在这种情况下,结果顺序将继续是未指定的,但这种情况很少见。
这个问题是关于 EcmaScript 2015 (ES6)。但需要注意的是,EcmaScript2017 规范删除了之前出现在规范中的以下段落Object.keys
,这里引用自EcmaScript 2016 规范:
如果实现为 for-in 语句定义了特定的枚举顺序,则必须对步骤 3 中返回的数组元素使用相同的顺序。
此外,EcmaScript 2020 规范从 部分中删除了以下段落,该部分EnumerableOwnPropertyNames
仍然出现在 EcmaScript 2019 规范中:
- 对属性元素进行排序,使它们的相对顺序与 Iterator 生成的相对顺序相同,如果使用O调用 EnumerateObjectProperties 内部方法,则返回该顺序。
这些清除意味着从EcmaScript的2020年起,Object.keys
实施统一的具体顺序Object.getOwnPropertyNames
和Reflect.ownKeys
,即在指定的OrdinaryOwnPropertyKeys。顺序是:
1个一种数组索引是一个字符串值属性密钥是一个规范的数字串2,其数值我是一个整数的范围0≤我<2 32 - 1。
2甲规范数字串是通过将产生一个数字的字符串表示ToString
,或字符串“-0”。例如,“012”不是规范数字字符串,但“12”是。
应该指出的是,所有主要的实现在几年前就已经与这个顺序保持一致。