方法的返回值类型是object_彻底理解Object.keys()

为什么Object.keys的返回值会自动排序?

例子是这样的:

  1. [/code]而下面这例子又不自动排序了?
  2. [code]复制代码

查了查ECMA262规范,再加上后来看了看这方面的文章,明白了为什么会发生这么诡异的事情。
故此写下这篇文章详细介绍,当Object.keys被调用时内部都发生了什么。
1. 答案

对于上面那个问题先给出结论,Object.keys在内部会根据属性名key的类型进行不同的排序逻辑。分三种情况:

如果属性名的类型是Number,那么Object.keys返回值是按照key从小到大排序如果属性名的类型是String,那么Object.keys返回值是按照属性被创建的时间升序排序。如果属性名的类型是Symbol,那么逻辑同String相同

这就解释了上面的问题。
下面我们详细介绍Object.keys被调用时,背后发生了什么。
2. 当Object.keys被调用时背后发生了什么

当Object.keys函数使用参数O调用时,会执行以下步骤:

第一步:将参数转换成Object类型的对象。

第二步:通过转换后的对象获得属性列表properties。

注意:属性列表properties为List类型(List类型是ECMAScript规范类型)

第三步:将List类型的属性列表properties转换为Array得到最终的结果。

规范中是这样定义的:

调用ToObject(O)将结果赋值给变量obj调用EnumerableOwnPropertyNames(obj, "key")将结果赋值给变量nameList调用CreateArrayFromList(nameList)得到最终的结果

2.1 将参数转换成Object(ToObject(O))

ToObject操作根据下表将参数O转换为Object类型的值:
参数类型 | 结果 |
--------------|------------------------|
Undefined | 抛出TypeError |
Null | 抛出TypeError |
Boolean | 返回一个新的 Boolean 对象 |
Number | 返回一个新的 Number 对象 |
String | 返回一个新的 String 对象 |
Symbol | 返回一个新的 Symbol 对象 |
Object | 直接将Object返回 |
因为Object.keys内部有ToObject操作,所以Object.keys其实还可以接收其他类型的参数。
上表详细描述了不同类型的参数将如何转换成Object类型。
我们可以简单写几个例子试一试:
先试试null会不会报错:

3ebe5ecba9fb800fdd269e957cce7975.png


图1 Object.keys(null)
如图1所示,果然报错了。
接下来我们试试数字的效果:

d0f61a2b34a81272b349dea9aafe3533.png


图2 Object.keys(123)
如图2所示,返回空数组。
为什么会返回空数组?请看图3:

e60962063e56573729aabb243723a6b9.png


图3 new Number(123)
如图3所示,返回的对象没有任何可提取的属性,所以返回空数组也是正常的。
然后我们再试一下String的效果:

d4eb1607a255e7dbd69ccee21cc0df5c.png


图4 Object.keys('我是Berwin')
图4我们会发现返回了一些字符串类型的数字,这是因为String对象有可提取的属性,看如图5:

a4b17a5d2d7361eaf3f9acf6832231a6.png


图5 new String('我是Berwin')
因为String对象有可提取的属性,所以将String对象的属性名都提取出来变成了列表返回出去了。
2.2 获得属性列表(EnumerableOwnPropertyNames(obj, "key"))

获取属性列表的过程有很多细节,其中比较重要的是调用对象的内部方法OwnPropertyKeys获得对象的ownKeys。

注意:这时的ownKeys类型是List类型,只用于内部实现

然后声明变量properties,类型也是List类型,并循环ownKeys将每个元素添加到properties列表中。

最终将properties返回。

您可能会感觉到奇怪,ownKeys已经是结果了为什么还要循环一遍将列表中的元素放到properties中。
这是因为EnumerableOwnPropertyNames操作不只是给Object.keys这一个API用,它内部还有一些其他操作,只是Object.keys这个API没有使用到,所以看起来这一步很多余。

所以针对Object.keys这个API来说,获取属性列表中最重要的是调用了内部方法OwnPropertyKeys得到ownKeys。

其实也正是内部方法OwnPropertyKeys决定了属性的顺序。

关于OwnPropertyKeys方法ECMA-262中是这样描述的:

当O的内部方法OwnPropertyKeys被调用时,执行以下步骤(其实就一步):

Return ! OrdinaryOwnPropertyKeys(O).

而OrdinaryOwnPropertyKeys是这样规定的:

声明变量keys值为一个空列表(List类型)把每个Number类型的属性,按数值大小升序排序,并依次添加到keys中把每个String类型的属性,按创建时间升序排序,并依次添加到keys中把每个Symbol类型的属性,按创建时间升序排序,并依次添加到keys中将keys返回(return keys)

上面这个规则不光规定了不同类型的返回顺序,还规定了如果对象的属性类型是数字,字符与Symbol混合的,那么返回顺序永远是数字在前,然后是字符串,最后是Symbol。
举个例子:
[code][/code]属性的顺序规则中虽然规定了Symbol的顺序,但其实Object.keys最终会将Symbol类型的属性过滤出去。(原因是顺序规则不只是给Object.keys一个API使用,它是一个通用的规则)
2.3 将List类型转换为Array得到最终结果(CreateArrayFromList( elements ))

现在我们已经得到了一个对象的属性列表,最后一步是将List类型的属性列表转换成Array类型。

将List类型的属性列表转换成Array类型非常简单:

先声明一个变量array,值是一个空数组循环属性列表,将每个元素添加到array中将array返回

3. 该顺序规则还适用于其他API

上面介绍的排序规则同样适用于下列API:

Object.entriesObject.valuesfor...in循环Object.getOwnPropertyNamesReflect.ownKeys

注意:以上API除了Reflect.ownKeys之外,其他API均会将Symbol类型的属性过滤掉。

文章来自:5分钟彻底理解Object.keys - IT程序员 翁笔

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值