Javascript的细节(二):取Object key的顺序
发表于更新于阅读时长 3 分钟不同浏览器和不同标准下取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 一词被去掉了,然而这并不意味着取键的顺序一定是有(跨浏览器通用的)顺序
在这个时代,访问某个对象所有键的方法有如下几种
Object.keys
Object.getOwnPropertyNames
for ... in
循环
但是查阅标准中相关章节,并没有涉及对顺序的规定,只是提到了 1 和 3 的顺序必须一样
3. ES6 时代
1在 ECMASCRIPT 6(发布于 2015 年,因此又被称为 ES2015)中,又增加了如下几种方法
- Object.getOwnPropertySymbols
- Reflect.ownKeys
查阅上述五种方法的相关标准,可以将它们分为两组
- 2 4 5 访问对象的内部方法[[OwnPropertyKeys]]
- 1 3 访问对象的内部方法[[Enumerate]]
标准中[[OwnPropertyKeys]]的定义如下
- Let keys be a new empty List.
- 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.
- 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.
- For each own property key P of O that is a Symbol, in property creation order Add P as the last element of keys.
- Return keys.
简要翻译如下
- 令 keys 为一空列表
- 先按照数值上升顺序加入所有为整数的键(确切说来,是所有可以被单目+转换成整数又不是 0 开头的字符串)
- 再按照添加顺序加入所有为字符串的键
- 最后按照添加顺序加入所有为
Symbol
的键 - 返回 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 中,下段代码
js
var obj = {y: 1,x: 2,'01': 3,1: 4}console.log(Object.keys(obj).join(''))
的返回值均为 "1yx01",读者可以自行在其他浏览器中测试
6. TL;DR
取一个对象的所有的 key 的顺序是有保证的,先按从小到大取数字(0 开头的不算),再按添加顺序取字符串
但是最好不要依赖这个顺序