object.keys循环问题

67 阅读2分钟

先直接讲一个结论,当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 会为每一个对象关联一个隐藏类,用于记录该对象的形状,相同形状的对象会共用同一个隐藏类。

  • 当对象添加、删除属性的时候,会创建一个新的对应的隐藏类,并重新关联。

  • 对象内属性会将部分常规属性直接放在对象第一层,所以它访问效率是最高的。

  • 常规属性的数量少于对象初始化时的属性数量时,常规属性会直接作为对象内属性存放。

  • 虽然快属性访问速度快,但是从线性结构中添加或删除时执行效率会非常低,因此如果属性特别多、或出现添加和删除属性时,就会将常规属性从线性存储改为字典存储,这就是慢属性

  • image.png ​ ​ 参考: 一行 Object.keys() 引发的血案