了解 Object.keys() 排序问题

了解 Object.keys() 排序问题

现象

偶然之间,返现 Object.keys() 的返回值是有序的,现象如下:

key 为 Number 类型时,输出的时候是有序的。

再来看下 key 为 String 类型时的情况:

key 为 String 类型时,按照定义先后顺序进行输出。

为什么会出现这种情况呢?直接去规范里找答案吧。

解析

根据规范,Object.keys() 有三步流程:

  1. Let obj be ? ToObject(O).

    通过调用 ToObject(O) 将参数转换为 Object 类型对象

  2. Let nameList be ? EnumerableOwnPropertyNames(obj, key).

    获取转换后对象的自有的可枚举属性列表 nameList

  3. Return CreateArrayFromList(nameList).

    将上一步得到的属性列表 nameList 转换为数组并返回

接来了了解下三个步骤中每个函数具体做了哪些事情。

ToObject(O)

ToObject() 主要是将参数转换为 Object 类型对象。

参数类型结果
Undefined抛出 TypeError
Null抛出 TypeError
Boolean返回一个新的 Boolean 对象
Number返回一个新的 Number 对象
String返回一个新的 String 对象
Symbol返回一个新的 Symbol 对象
BigInt返回一个新的 BigInt 对象
Object直接将参数返回

EnumerableOwnPropertyNames()

通过 EnumerableOwnPropertyNames() 获取对象自有的可枚举属性列表 properties

  1. 通过 ownPropertyKeys() 获取对象自有属性 ownKeys

    OwnPropertyKeys() 内部调用的是 OridinaryOwnPropertyKeys() 获取 ownKeys

    1. OrdinaryOwnPropertyKeys()

      • 对于 Number 类型属性,按照数值大小升序排序
      • 对于 String 类型属性,按定义的先后顺序排序
      • 对于 Symbol 类型属性,按定义的先后顺序排序
  2. 遍历 ownKeys ,获取其中可枚举的属性

    遍历过程中有如下处理:

    • 排除非 String 类型的属性,如:Symbol
    • 获取属性的描述对象 desc,判断属性是否可枚举

CreateArrayFromList()

EnumerableOwnPropertyNames() 返回的是 List 类型,List 类型只是用于内部实现,需要通过 CreateArrayFromList() 将其转换为 Array 类型。

总结

  • 上面的排序实现也适用于以下 API

    • Object.entries()

    • for...in

    • Object.getOwnPropertyNames()

    • Object.getOwnPropertySymbols()

      只输出 Symbol 类型属性

    • Reflect.ownKeys()

      Reflect.ownKeys() 输出所有类型的属性