「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战」。
Object.keys() 是干嘛的
- 其实不用过多介绍,大家或多或少都使用过 Object.keys()
- 它接受一个对象,返回其可枚举的属性名的列表,包括该对象继承的属性
- MDN 上有说,它输出的属性名顺序与正常使用 for/in 循环遍历的顺序相同,但是没有说是按什么顺序排列的
- 既然输出的是一个属性名的列表,那列表会不会涉及到某种排序规则呢?
- 闲来无事,自己探究一波
Object.keys() 返回的数组中,属性名是如何排序的?
- 先写个代码跑一下
class A {
constructor() {
this.a = "";
}
}
class B extends A {
constructor() {
super();
this.b = "";
}
}
const obj = new B();
console.log(Object.keys(obj));
- 毫无疑问,上面打印的数组为
[ 'a', 'b' ] - 那为什么是
[ 'a', 'b' ],而不是[ 'b', 'a' ]呢? - 我们修改一下代码,然后输出看看
class A {
constructor() {
this.b = "";
}
}
class B extends A {
constructor() {
super();
this.a = "";
}
}
const obj = new B();
console.log(Object.keys(obj));
-
执行后发现,现在输出的是
[ 'b', 'a' ],输出结果变了,但是输出的属性名顺序没变 -
依然是先输出的类 A 上的属性,然后输出类 B 上的属性
-
我们都知道,es6 中类的继承过程是这样的
- 会先创建实例,然后将父类的属性和方法加到实例上
- 然后才是将子类的属性和方法加到实例上
-
由于对象先添加的属性 b,再添加的属性 a, 所以输出
[ 'b', 'a' ]很合理 -
那我们可以认为 Object.keys() 输出的列表是按照原对象属性添加的先后顺序来输出的吗?
-
再来个栗子 🌰,大家看看输出的是啥
const obj = {};
obj["d"] = "";
obj["a"] = "";
obj["b"] = "";
obj["c"] = "";
console.log(Object.keys(obj));
-
上面输出的是
[ 'd', 'a', 'b', 'c' ] -
符合上面的猜测,输出很合理
-
再来个栗子 🌰,大家看看输出的是啥
const obj = {};
obj[4] = "";
obj[1] = "";
obj[3] = "";
obj[2] = "";
console.log(Object.keys(obj));
- 上面输出的是
[ '1', '2', '3', '4' ] - 神奇的事情发生了,为啥不是
[ '4', '1', '3', '2' ]呢? - 如果我们将上面两个例子合并一下输出的结果又是什么呢?
const obj = {};
obj["d"] = "";
obj["a"] = "";
obj["b"] = "";
obj["c"] = "";
obj[4] = "";
obj[1] = "";
obj[3] = "";
obj[2] = "";
console.log(Object.keys(obj));
-
上面输出的是
['1', '2', '3', '4', 'd', 'a', 'b', 'c' ] -
这个结果与上面的例子似乎有着某种相似性,但是有说不出个所以然
-
这个时候直觉告诉我,需要开始找资料了
-
于是我开始一番搜索后发现,MDN 上有写,Object.keys() 的 Polyfill 实现中,就是用 for/in 实现的,所以 for/in 输出结果的顺序就是 Object.keys() 输出的顺序
-
根据 MDN 提供的关于 Object.keys() 的 ECMAScript 规范链接,一层一层找下去,能够看到如下规则:
-
翻译过来大概意思就是:
- 第一步,会创建一个空列表 keys,用于存放属性名
- 第二步,将属性名是合法数组索引值的,按照升序依次存入列表
- 第三步,将属性名是字符串类型的,按照创建的时间先后顺序依次存入列表
- 第四步,将属性名是 Symbol 类型的,也按照创建的时间先后顺序一次存入列表
- 第五步,返回列表 keys
-
这么看来,上面的输出结果就非常合理了
-
那接下来我们最后看一个例子会输出什么
const obj = {};
obj["d"] = "";
obj["a"] = "";
obj["b"] = "";
obj["c"] = "";
obj[4] = "";
obj[1] = "";
obj[3] = "";
obj[2] = "";
obj[0.9] = "";
obj[Symbol("a")] = "";
obj[Symbol("b")] = "";
obj[Symbol(2)] = "";
obj[Symbol(1)] = "";
console.log(Object.keys(obj));
- 上面输出的是
['1', '2', '3', '4', 'd', 'a', 'b', 'c', '0.9' ] - 神奇的事情又发生了
- 0.9 不也是数字吗,为啥在最后面
- Symbol 类型的属性名呢,咋没有了
- 我们看下上面规则的第二步,说的是合法数组索引值,其实说的就是正整数,0.9 不是正整数,所以被当作字符类型处理了
- 那 Symbol 呢?
- 上面我们说到了,Object.keys() 的实现中使用了 for/in
- MDN 上有说到,for/in 输出的结果是不包括 Symbol 类型的,要输出 Symbol 类型属性需要用到
getOwnPropertySymbols方法 - 这样看来,上面的例子也就都合理了😄
最后
-
Object.keys() 的分享就到这里,怎么样它输出的结果的顺序规则,你 xuefei 了吗👀
-
如果觉得文章写的不错的话,希望大家不要吝惜点赞,大家的鼓励是我分享的最大动力 🥰