Javascript的细节(二): 取Object key的顺序

发表于更新于阅读时长 4 分钟

不同浏览器和不同标准下取 Object 的所有 key 的顺序

1. ES3 时代

ECMASCript 3(发布于 1999 年)中,对于 Object 有如下的描述

An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive value, object, or function. A function stored in a property of an object is called a method.

翻译如下

一个对象是 Object 类型的一个成员.它是一个无序的集合,其属性可能包含基本数据类型,对象,或者函数.存储在对象属性里的函数被称为方法

因此在过去的时代里,取一个对象的所有键并不存在一个通用的顺序,甚至不存在一个稳定的顺序

2. ES5 时代

ECMAScript 5.1(发布于 2011 年)中,对 Object 的描述改成了

An object is a collection of properties and has a single prototype object. The prototype may be the null value.

可以看到 unordered 一词被去掉了,然而这并不意味着取键的顺序一定是有(跨浏览器通用的)顺序

在这个时代,访问某个对象所有键的方法有如下几种

  1. Object.keys
  2. Object.getOwnPropertyNames
  3. for ... in 循环

但是查阅标准中相关章节,并没有涉及对顺序的规定,只是提到了 1 和 3 的顺序必须一样

3. ES6 时代

1ECMASCRIPT 6(发布于 2015 年,因此又被称为 ES2015)中,又增加了如下几种方法

  1. Object.getOwnPropertySymbols
  2. Reflect.ownKeys

查阅上述五种方法的相关标准,可以将它们分为两组

  • 2,4,5 访问对象的内部方法[[OwnPropertyKeys]]
  • 1,3 访问对象的内部方法[[Enumerate]]

标准中[[OwnPropertyKeys]]的定义如下

  1. Let keys be a new empty List.
  2. For each own property key P of O that is an integer index, in ascending numeric index order. Add P as the last element of keys.
  3. For each own property key P of O that is a String but is not an integer index, inproperty creation order. Add P as the last element of keys.
  4. For each own property key P of O that is a Symbol, in property creation order Add P as the last element of keys.
  5. Return keys.

简要翻译如下

  1. 令 keys 为一空列表
  2. 先按照数值上升顺序加入所有为整数的键(确切说来,是所有可以被单目+转换成整数又不是 0 开头的字符串)
  3. 再按照添加顺序加入所有为字符串的键
  4. 最后按照添加顺序加入所有为 Symbol 的键
  5. 返回 keys

可以看出第一组方法,它们取键的顺序是可以保证的

而对[[Enumerate]]的定义则比较复杂,但其中写到

The mechanics and order of enumerating the properties is not specified but must conform to the rules specified below

可以看到第二组方法,取键的顺序是没有保证的.根据一些帖子,这是为了保持对 ES5 的兼容性

当然在 ES6 中可以使用取键顺序一定按照添加顺序的新类型,Map

4. 当代

在目前的标准中,对于上述五种方法,均取 [[OwnPropertyKeys]]的值,而该内部方法则访问 OrdinaryOwnPropertyKeys,它遵循在 3 中提到的数字->字符串->符号的顺序

5. 现实生活中

经测试,在 Firefox 60, Chrome66, Edge17 和 IE 11 中, 下段代码

var obj = {
  y: 1,
  x: 2,
  '01': 3,
  '1': 4
}
console.log(Object.keys(obj).join(''))

的返回值均为 "1yx01",读者可以自行在其他浏览器中测试

6. TL;DR

取一个对象的所有的 key 的顺序是有保证的,先按从小到大取数字(0 开头的不算),再按添加顺序取字符串

但是最好不要依赖这个顺序


  1. 部分内容参考该文``
© 2016 - 2022Austaras Devas