We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
前几天一个朋友问了我一个问题:为什么Object.keys的返回值会自动排序?
Object.keys
例子是这样的:
const obj = { 100: '一百', 2: '二', 7: '七' } Object.keys(obj) // ["2", "7", "100"]
而下面这例子又不自动排序了?
const obj = { c: 'c', a: 'a', b: 'b' } Object.keys(obj) // ["c", "a", "b"]
当朋友问我这个问题时,一时间我也回答不出个所以然。故此去查了查ECMA262规范,再加上后来看了看这方面的文章,明白了为什么会发生这么诡异的事情。
故此写下这篇文章详细介绍,当Object.keys被调用时内部都发生了什么。
对于上面那个问题先给出结论,Object.keys在内部会根据属性名key的类型进行不同的排序逻辑。分三种情况:
key
Number
String
Symbol
这就解释了上面的问题。
下面我们详细介绍Object.keys被调用时,背后发生了什么。
当Object.keys函数使用参数O调用时,会执行以下步骤:
O
第一步:将参数转换成Object类型的对象。
Object
第二步:通过转换后的对象获得属性列表properties。
properties
注意:属性列表properties为List类型(List类型是ECMAScript规范类型)
第三步:将List类型的属性列表properties转换为Array得到最终的结果。
规范中是这样定义的: 调用ToObject(O)将结果赋值给变量obj 调用EnumerableOwnPropertyNames(obj, "key")将结果赋值给变量nameList 调用CreateArrayFromList(nameList)得到最终的结果
规范中是这样定义的:
ToObject(O)
obj
EnumerableOwnPropertyNames(obj, "key")
nameList
CreateArrayFromList(nameList)
ToObject操作根据下表将参数O转换为Object类型的值:
ToObject
因为Object.keys内部有ToObject操作,所以Object.keys其实还可以接收其他类型的参数。
上表详细描述了不同类型的参数将如何转换成Object类型。
我们可以简单写几个例子试一试:
先试试null会不会报错:
null
图1 Object.keys(null)
Object.keys(null)
如图1所示,果然报错了。
接下来我们试试数字的效果:
图2 Object.keys(123)
Object.keys(123)
如图2所示,返回空数组。
为什么会返回空数组?请看图3:
图3 new Number(123)
new Number(123)
如图3所示,返回的对象没有任何可提取的属性,所以返回空数组也是正常的。
然后我们再试一下String的效果:
图4 Object.keys('我是Berwin')
Object.keys('我是Berwin')
图4我们会发现返回了一些字符串类型的数字,这是因为String对象有可提取的属性,看如图5:
图5 new String('我是Berwin')
new String('我是Berwin')
因为String对象有可提取的属性,所以将String对象的属性名都提取出来变成了列表返回出去了。
获取属性列表的过程有很多细节,其中比较重要的是调用对象的内部方法OwnPropertyKeys获得对象的ownKeys。
OwnPropertyKeys
ownKeys
注意:这时的ownKeys类型是List类型,只用于内部实现
然后声明变量properties,类型也是List类型,并循环ownKeys将每个元素添加到properties列表中。
最终将properties返回。
您可能会感觉到奇怪,ownKeys已经是结果了为什么还要循环一遍将列表中的元素放到properties中。 这是因为EnumerableOwnPropertyNames操作不只是给Object.keys这一个API用,它内部还有一些其他操作,只是Object.keys这个API没有使用到,所以看起来这一步很多余。
您可能会感觉到奇怪,ownKeys已经是结果了为什么还要循环一遍将列表中的元素放到properties中。
这是因为EnumerableOwnPropertyNames操作不只是给Object.keys这一个API用,它内部还有一些其他操作,只是Object.keys这个API没有使用到,所以看起来这一步很多余。
所以针对Object.keys这个API来说,获取属性列表中最重要的是调用了内部方法OwnPropertyKeys得到ownKeys。
其实也正是内部方法OwnPropertyKeys决定了属性的顺序。
关于OwnPropertyKeys方法ECMA-262中是这样描述的:
当O的内部方法OwnPropertyKeys被调用时,执行以下步骤(其实就一步):
Return ! OrdinaryOwnPropertyKeys(O).
而OrdinaryOwnPropertyKeys是这样规定的:
OrdinaryOwnPropertyKeys
keys
return keys
上面这个规则不光规定了不同类型的返回顺序,还规定了如果对象的属性类型是数字,字符与Symbol混合的,那么返回顺序永远是数字在前,然后是字符串,最后是Symbol。
举个例子:
Object.keys({ 5: '5', a: 'a', 1: '1', c: 'c', 3: '3', b: 'b' }) // ["1", "3", "5", "a", "c", "b"]
属性的顺序规则中虽然规定了Symbol的顺序,但其实Object.keys最终会将Symbol类型的属性过滤出去。(原因是顺序规则不只是给Object.keys一个API使用,它是一个通用的规则)
CreateArrayFromList( elements )
现在我们已经得到了一个对象的属性列表,最后一步是将List类型的属性列表转换成Array类型。
将List类型的属性列表转换成Array类型非常简单:
array
上面介绍的排序规则同样适用于下列API:
Object.entries
Object.values
for...in
Object.getOwnPropertyNames
Reflect.ownKeys
注意:以上API除了Reflect.ownKeys之外,其他API均会将Symbol类型的属性过滤掉。
The text was updated successfully, but these errors were encountered:
今天就遇到这个问题了 很不错
Sorry, something went wrong.
berwin
No branches or pull requests
5分钟彻底理解Object.keys
前几天一个朋友问了我一个问题:为什么
Object.keys
的返回值会自动排序?例子是这样的:
而下面这例子又不自动排序了?
当朋友问我这个问题时,一时间我也回答不出个所以然。故此去查了查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
。第三步:将List类型的属性列表
properties
转换为Array得到最终的结果。2.1 将参数转换成Object(
ToObject(O)
)ToObject
操作根据下表将参数O
转换为Object类型的值:因为
Object.keys
内部有ToObject
操作,所以Object.keys
其实还可以接收其他类型的参数。上表详细描述了不同类型的参数将如何转换成Object类型。
我们可以简单写几个例子试一试:
先试试
null
会不会报错:图1
Object.keys(null)
如图1所示,果然报错了。
接下来我们试试数字的效果:
图2
Object.keys(123)
如图2所示,返回空数组。
为什么会返回空数组?请看图3:
图3
new Number(123)
如图3所示,返回的对象没有任何可提取的属性,所以返回空数组也是正常的。
然后我们再试一下String的效果:
图4
Object.keys('我是Berwin')
图4我们会发现返回了一些字符串类型的数字,这是因为String对象有可提取的属性,看如图5:
图5
new String('我是Berwin')
因为String对象有可提取的属性,所以将String对象的属性名都提取出来变成了列表返回出去了。
2.2 获得属性列表(
EnumerableOwnPropertyNames(obj, "key")
)获取属性列表的过程有很多细节,其中比较重要的是调用对象的内部方法
OwnPropertyKeys
获得对象的ownKeys
。然后声明变量
properties
,类型也是List类型,并循环ownKeys
将每个元素添加到properties
列表中。最终将
properties
返回。所以针对
Object.keys
这个API来说,获取属性列表中最重要的是调用了内部方法OwnPropertyKeys
得到ownKeys
。其实也正是内部方法
OwnPropertyKeys
决定了属性的顺序。关于
OwnPropertyKeys
方法ECMA-262中是这样描述的:当
O
的内部方法OwnPropertyKeys
被调用时,执行以下步骤(其实就一步):Return ! OrdinaryOwnPropertyKeys(O).
而
OrdinaryOwnPropertyKeys
是这样规定的:keys
值为一个空列表(List类型)keys
中keys
中keys
中keys
返回(return keys
)上面这个规则不光规定了不同类型的返回顺序,还规定了如果对象的属性类型是数字,字符与Symbol混合的,那么返回顺序永远是数字在前,然后是字符串,最后是Symbol。
举个例子:
属性的顺序规则中虽然规定了
Symbol
的顺序,但其实Object.keys
最终会将Symbol
类型的属性过滤出去。(原因是顺序规则不只是给Object.keys
一个API使用,它是一个通用的规则)2.3 将List类型转换为Array得到最终结果(
CreateArrayFromList( elements )
)现在我们已经得到了一个对象的属性列表,最后一步是将List类型的属性列表转换成Array类型。
将List类型的属性列表转换成Array类型非常简单:
array
,值是一个空数组array
中array
返回3. 该顺序规则还适用于其他API
上面介绍的排序规则同样适用于下列API:
Object.entries
Object.values
for...in
循环Object.getOwnPropertyNames
Reflect.ownKeys
The text was updated successfully, but these errors were encountered: