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

时间:2015-05-06 12:05:29

标签: 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?

3 个答案:

答案 0 :(得分:58)

对于for-inObject.keys否。

对于其他一些操作:,通常。

虽然ES6 / ES2015添加了属性顺序,但由于旧版兼容性问题,它不需要for-inObject.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.getOwnPropertyNamesObject.getOwnPropertySymbolsReflect.ownKeys,按照以下顺序执行普通对象:

  1. 整数索引(如果适用),按升序排列。
  2. 其他字符串键(如果适用),在属性创建顺序中。
  3. 符号键(如果适用),在属性创建顺序中。
  4. 此行为在[[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

  1. properties 的元素进行排序,以便它们与Iterator产生的相对顺序相同,如果用 O 调用EnumerateObjectProperties内部方法,则该迭代器将返回这些相对顺序。

这些删除操作意味着从EcmaScript 2020起,Object.keys强制执行与Object.getOwnPropertyNamesReflect.ownKeys相同的特定顺序,即OrdinaryOwnPropertyKeys中指定的顺序。顺序是:

  1. 属于数组索引 1 (按升序数字索引)的自身属性
  2. 其他自己的String属性,以创建属性的时间顺序升序
  3. 以创建属性的时间顺序升序排列的自己的符号属性

1 array index是一个字符串值的属性键,它是规范数字String 2 ,其数值 i 为范围+0≤ i <2 32 -1的整数。

2 canonical numeric StringToString或字符串“ -0”将产生的数字的字符串表示形式。例如,“ 012”不是规范的数字字符串,而是“ 12”。

应该注意,所有主要实现都已经在几年前与该命令保持一致。

答案 2 :(得分:4)

如其他答案所述,ES2015并未为(非常常用的)属性迭代方法for-inObject.keysJSON.stringify定义枚举顺序,而 为其他方法(如Reflect.ownKeys)定义了枚举方法。 但是,这种不一致很快将不再存在,并且所有属性迭代方法将以可预测的方式进行迭代。

正如许多人在使用JS和注释中所观察到的那样,尽管上述方法的规范并未保证属性迭代的顺序,但是每个实现几乎总是以相同的确定性顺序进行迭代。结果,有一个(完成的)提案可以更改规范以使此行为正式生效:

Specifying for-in enumeration orderStage 4

使用此建议,在大多数情况下,保证for..inObject.keys / values / entriesJSON.stringify依次迭代:< / p>

(1)数值数组键

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

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

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

specification text很简单:EnumerateObjectProperties,由for..in等调用的有问题的抽象方法,其顺序使用未指定,{{3 }}调用will now,这是为其指定迭代顺序 的内部方法。

目前有几种奇怪的情况,实现尚无法达成共识,在这种情况下,产生的顺序将[[OwnPropertyKeys]],但这种情况很少。 >