ES6是否为对象属性引入了明确定义的枚举顺序?

Ben*_*Ben 50 javascript ecmascript-6

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?
Run Code Online (Sandbox Code Playgroud)

Ori*_*iol 68

对于for-inObject.keys:不.

对于一些其他操作:是的,通常.

虽然ES6/ES2015添加了属性订单,但由于遗留兼容性问题,它不需要JSON.stringifyfor-in遵循该订单.

Object.keys循环根据[[Enumerate]]迭代,[定义为(强调我的):

当调用O的[[Enumerate]]内部方法时,采取以下步骤:

返回一个Iterator对象(25.1.1.2),其下一个方法迭代O的可枚举属性的所有String值键.Iterator对象必须从%IteratorPrototype%(25.1.2)继承.枚举属性的机制和顺序没有指定,但必须符合下面规定的规则[1].

ES7/ES2016删除[[Enumerate]]内部方法,而是使用抽象操作EnumerateObjectProperties,但就像[[Enumerate]]一样,它没有指定任何顺序.

并且还可以看到以下引用JSON.stringify:

如果实现为for-in语句定义了特定的枚举顺序,[...]

这意味着不需要实现来定义特定的枚举顺序.ECMAScript 2015语言规范项目编辑Allen Wirfs-Brock在规范完成后发布的帖子中证实了这一点.

其他操作,如for-in,Object.keysObject.getOwnPropertyNames,不遵循普通对象的顺序如下:

  1. 整数索引(如果适用),按升序排列.
  2. 属性创建顺序中的其他字符串键(如果适用).
  3. 属性创建顺序中的符号键(如果适用).

此行为在[[OwnPropertyKeys]]内部方法中定义.但要注意外来物体可能会以不同方式定义该方法,例如

console.log(Reflect.ownKeys(new Proxy({}, {
  ownKeys: () => ['3','1','2']
}))); // ['3','1','2'], the integer indices are not sorted!
Run Code Online (Sandbox Code Playgroud)


[1]下面说:

[[Enumerate]]必须获取目标对象的属性键, 就像调用其[[OwnPropertyKeys]]内部方法一样.

并且[[OwnPropertyKeys]]的顺序是明确定义的.但是不要让这让你感到困惑:"好像"只意味着"相同的属性",而不是"相同的顺序".

这可以在EnumerableOwnNames中看到,它使用[[OwnPropertyKeys]]来获取属性,然后命令它们

如果调用了[[Enumerate]]内部方法,将返回迭代器生成的相对顺序

如果[[Enumerate]]需要以与[[OwnPropertyKeys]]相同的顺序进行迭代,则不需要重新排序.

  • 值得注意的是,虽然规范不需要`for-in`或`Object.keys`来遵循顺序,但当前版本的Firefox,Chrome和Edge都可以:http://jsfiddle.net/arhbn3k2/1/这是有道理的,拥有多个枚举实现会很奇怪.该规范并不需要它,因为不同的引擎已经具有与新定义的顺序不同的行为,并且委员会不希望要求实现可能破坏现有代码.但是,实现似乎决定了; Firefox的订单当然不同. (5认同)
  • 只是好奇`for-in`或`Object.keys`的订单要求是*遗留兼容性问题*? (3认同)
  • 我实际上找不到 getOwnPropertyNames 如何保证订单的信息?实际上,Firefox 和 Chrome 返回的是 `Object.getOwnPropertyNames({ 20 : 'a', 10 : 'b' })` 的 `[ "10", "20" ]` 数字顺序,而不是书面顺序。 (2认同)
  • @ user10089632哈哈!很好的问题......答案*可能是*应该*被破坏的旧代码可以正常工作......至少在这方面!我认为,这可能会导致例外情况不被抛出,并完全改变行为.我怀疑更新的规格是否,或者可能是,在所有情况下都是如此严格. (2认同)
  • @52d6c6af - 这是正确的,因为 **ES2015** 不需要 `Object.keys` 等来遵循新的顺序。这是不正确的,因为 ES2020 **现在**需要它,尤其是因为引擎已经更新(请参阅上面我在 2017 年的评论)来这样做。由于 Oriol 不再为 SO 做出贡献,我在答案的顶部添加了一条注释。 (2认同)

Cer*_*nce 11

作为覆盖对方的回答,ES2015不会为(很常用的)属性来定义枚举顺序迭代方法for-inObject.keysJSON.stringify,而它定义好其他方法,如枚举的方法Reflect.ownKeys但是,这种不一致很快就会不复存在,所有属性迭代方法都会以可预测的方式进行迭代。

正如许多人在他们自己的 JS 经验和评论中所观察到的那样,尽管上述这些方法的规范不保证属性迭代顺序,但无论如何,每个实现几乎总是以相同的确定性顺序进行迭代。因此,有一个(已完成的)提议来更改规范以使此行为正式化:

指定 for-in 枚举顺序第 4 阶段

这个建议,在大多数情况下,for..inObject.keys/ values/ entries,并JSON.stringify保证迭代,以便在:

(1) 数字数组键

(2) 非符号键,按插入顺序

(3) 符号键,按插入顺序

这与Reflect.ownKeys已经保证以这种方式迭代的其他方法的顺序相同。

规范的文本是相当简单的:EnumerateObjectProperties通过调用的问题抽象方法for..in等,其顺序使用是未指定的,现在将调用[[OwnPropertyKeys]],这是它的迭代顺序内部方法指定。

有一些奇怪的情况,目前的实现同意,在这种情况下,结果顺序将继续是未指定的,但这种情况很少见。


tri*_*cot 8

这个问题是关于 EcmaScript 2015 (ES6)。但需要注意的是,EcmaScript2017 规范删除了之前出现在规范中的以下段落Object.keys,这里引用自EcmaScript 2016 规范

如果实现为 for-in 语句定义了特定的枚举顺序,则必须对步骤 3 中返回的数组元素使用相同的顺序。

此外,EcmaScript 2020 规范从 部分中删除了以下段落,该部分EnumerableOwnPropertyNames仍然出现在 EcmaScript 2019 规范中

  1. 属性的元素进行排序,使它们的相对顺序与 Iterator 生成的相对顺序相同,如果使用O调用 EnumerateObjectProperties 内部方法,则返回该顺序。

这些清除意味着从EcmaScript的2020年起,Object.keys实施统一的具体顺序Object.getOwnPropertyNamesReflect.ownKeys,即在指定的OrdinaryOwnPropertyKeys。顺序是:

  1. 自己的属性是数组索引1在上升的数字顺序索引
  2. 其他自己的 String 属性,按属性创建的时间升序排列
  3. 自己的符号属性,按属性创建的时间升序排列

1个一种数组索引是一个字符串值属性密钥是一个规范的数字串2,其数值的范围是从0的整数 < 2 32 - 1。

2规范数字串是通过将产生一个数字的字符串表示ToString,或字符串“-0”。例如,“012”不是规范数字字符串,但“12”是。

应该指出的是,所有主要的实现在几年前就已经与这个顺序保持一致。