for...in和Object.keys迭代顺序的深入分析
先说结论
for...in和Object.keys对可枚举属性进行遍历的顺序与属性的书写顺序不一定一致,因为在内部会根据属性key值的类型进行不同的排序逻辑,分三种情况:
- 如果属性名的类型是Number,那么Object.keys返回值是按照key从小到大排序
- 如果属性名的类型是String,那么Object.keys返回值是按照属性被创建的时间升序排序。
背后的执行机制
对象的属性可分为常规属性和排序属性两种,在ECMAScript规范中定义了 「数字属性应该按照索引值大小升序排列,字符串属性根据创建时的顺序升序排列。」在这⾥我们把对象中的数字属性称为「排序属性」,在V8中被称为elements,字符串属性就被称为「常规属性」, 在V8中被称为properties。在V8内部,为了有效地提升存储和访问这两种属性的性能,分别使⽤了两个线性数据结构来分别保存排序属性和常规属性。
以下面这段代码为例进行说明:
1 | function Foo() { |
在V8内部的存储结构如下图所示:
在elements对象中,会按照顺序存放排序属性,properties属性则指向了properties对 象,在properties对象中,会按照创建时的顺序保存了常规属性。