Object.keys()返回的顺序是什么?
我们理解的 Object.keys()总是会按照实际创建属性的顺序返回
当key为字符串时候,返回
let test = {
a: 1,
b: 2,
d: 4,
c: 3,
}
console.log(Object.keys(test))
// 输出[ 'a', 'b', 'd', 'c' ]
把key换成浮点数,结果如下
let test = {
'1.0': 1,
'2.0': 2,
'4.0': 4,
'3.0': 3,
}
console.log(Object.keys(test))
// 输出 [ '1.0', '2.0', '4.0', '3.0' ]
把key换成正整数,结果如下
let test = {
[1]: 1,
[2]: 2,
[4]: 4,
[3]: 3,
}
console.log(Object.keys(test))
// 输出 [ '1', '2', '3', '4' ]
把key换成浮点数和正整数组合,结果如下:
let test = {
'1.0': 1,
[2]: 2,
'4.0': 4,
[3]: 3,
}
console.log(Object.keys(test))
//输出 [ '2', '3', '1.0', '4.0' ]
通过上面的输出,总结出现象是如果key为字符串或者浮点数的时候,会按照创建属性的顺序返回,如果是正整数会按照从小到大的顺序返回。这种情况的产生的原因是什么?
Object.keys() 返回顺序与遍历对象属性时的顺序一样,调用的 [OwnPropertyKeys] 内部方法
根据 ECMAScript 规范,在输出 keys 时会先将所有 key 为数组索引类型(正整数)从小到大的顺序排序,然后将所有字符串类型(包括负数、浮点数)的 key 按照实际创建的顺序来排序。
V8 内部是如何处理对象属性的?
V8 在存储对象属性时,为了提高访问效率,会分为常规属性(properties) 和 排序属性(elements)
排序属性(elements) ,就是数组索引类型的属性(也就是正整数类型)。
常规属性(properties) ,就是字符串类型的属性(也包括负数、浮点数)。
以上两种属性都会存放在线性结构中,称为快属性。
然而这样每次查询都有一个间接层,会影响效率,所以 V8 引入对象内属性(in-object-properties) 。
V8 会为每一个对象关联一个隐藏类,用于记录该对象的形状,相同形状的对象会共用同一个隐藏类。
当对象添加、删除属性的时候,会创建一个新的对应的隐藏类,并重新关联。
对象内属性会将部分常规属性直接放在对象第一层,所以它访问效率是最高的。
当常规属性的数量少于对象初始化时的属性数量时,常规属性会直接作为对象内属性存放。
虽然快属性访问速度快,但是从线性结构中添加或删除时执行效率会非常低,因此如果属性特别多、或出现添加和删除属性时,就会将常规属性从线性存储改为字典存储,形成慢属性。