先直接讲一个结论,当object.keys所作用的对象中的key是正整数的话,那么将不会按照预期的对象内创建顺序输出,同理object.values,和object.entries,例如下
var a ={
'1': 'a', //这里的1无所谓是'1'还是1
'3': 'b',
'2': 'c'
}
Object.keys(a)
// 此时输出['1','2','3']
然后来看下直观的解释
-
Object.keys() 返回顺序与遍历对象属性时的顺序一样,调用的 [OwnPropertyKeys] 内部方法。
-
根据 ECMAScript 规范,在输出 keys 时会先将所有 key 为数组索引类型(正整数)从小到大的顺序排序,然后将所有字符串类型(包括负数、浮点数)的 key 按照实际创建的顺序来排序。但是为什么呢,这就需要往深层去看了。 V8 内部是如何处理对象属性的?
-
V8 在存储对象属性时,为了提高访问效率,会分为常规属性(properties) 和 排序属性(elements)
-
排序属性(elements) ,就是数组索引类型的属性(也就是正整数类型)。
-
常规属性(properties) ,就是字符串类型的属性(也包括负数、浮点数)。
-
以上两种属性都会存放在线性结构中,称为快属性。
-
然而这样每次查询都有一个间接层,会影响效率,所以 V8 引入对象内属性(in-object-properties) 。
-
V8 会为每一个对象关联一个隐藏类,用于记录该对象的形状,相同形状的对象会共用同一个隐藏类。
-
当对象添加、删除属性的时候,会创建一个新的对应的隐藏类,并重新关联。
-
对象内属性会将部分常规属性直接放在对象第一层,所以它访问效率是最高的。
-
当常规属性的数量少于对象初始化时的属性数量时,常规属性会直接作为对象内属性存放。
-
虽然快属性访问速度快,但是从线性结构中添加或删除时执行效率会非常低,因此如果属性特别多、或出现添加和删除属性时,就会将常规属性从线性存储改为字典存储,这就是慢属性。
-
参考: 一行 Object.keys() 引发的血案