自有属性的枚举顺序

ES5 并没有定义对象属性的枚举顺序,而是把该问题留给了 JS 引擎厂商。而 ES6 则严格定义了对象自有属性在被枚举时返回的顺序。这对 Object.getOwnPropertyNames()Reflect.ownKeys(详见第十二章)如何返回属性造成了影响,还同样影响了 Object.assign() 处理属性的顺序。

自有属性枚举时基本顺序如下:

  1. 所有的数字类型键,按升序排列。

  2. 所有的字符串类型键,按被添加到对象的顺序排列。

  3. 所有的符号类型(详见第六章)键,也按添加顺序排列。

这里有个示例:

var obj = {
    a: 1,
    0: 1,
    c: 1,
    2: 1,
    b: 1,
    1: 1
};

obj.d = 1;

console.log(Object.getOwnPropertyNames(obj).join(""));     // "012acbd"

Object.getOwnPropertyNames() 方法按 0、1、2、a、c、b、d 的顺序返回了 obj 对象的属性。注意,数值类型的键会被合并并排序,即使这未遵循在对象字面量中的顺序。字符串类型的键会跟在数值类型的键之后,按照被添加到 obj 对象的顺序,在对象字面量中定义的键会首先出现,接下来是此后动态添加到对象的键。

for-in 循环的枚举顺序仍未被明确规定,因为并非所有的 JS 引擎都采用相同的方式。而 Object.keys()JSON.stringify() 也使用了与 for-in 一样的枚举顺序。

虽然枚举顺序的变动对 JS 的工作方式影响甚小,但是依赖于特定枚举顺序才能正确运行的程序并不罕见。因此 ES6 通过规定枚举的顺序,以确保依赖枚举操作的 JS 代码都能正常工作,而不用在意其运行环境。