Object.keys的‘诡异’特性,你值得收藏!
先从‘诡异’的问题入手
- 例1: 纯Number类型的属性
const obj = {
1: 1,
6: 6,
3: 3,
2: 2
}
console.log('keys', Object.keys(obj))
// ['1', '2', '3', '6']
返回的key为什么自动按照升序排序了?
- 例2: 纯String类型的属性
const obj2 = {
a: 'a',
c: 'c',
f: 'f',
b: 'b',
}
console.log(Object.keys(obj2))
// ['a', 'c', 'f', 'b']
这里为什么又不自动排序了?
看到这里是不是觉得很懵?话不多说,我们先查文档,看看mdn上对Object.keys
的描述:
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
emm,然而它并没有说到底是按哪种顺序返回的。
探索
既然文档上找不到,那我们就一步一步来慢慢研究
Object.keys的polyfill的实现
if (!Object.keys) {
Object.keys = (function () {
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function (obj) {
if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
var result = [];
for (var prop in obj) {
if (hasOwnProperty.call(obj, prop)) result.push(prop);
}
if (hasDontEnumBug) {
for (var i=; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
}
}
return result;
}
})()
};
从Object.keys
的polyfill的实现,我们可以发现它内部其实是用for...in
来实现的。那我们就可以去查找for...in
遍历时的顺序规则。然而它也并没有介绍遍历的顺序是怎样的,那么我们就只能去查找ECMAScript
的规范了。
Object.keys的规范定义
- 调用
ToObject(O)
将结果赋值给变量obj
- 调用
EnumerableOwnPropertyNames(obj, "key")
将结果赋值给变量nameList
- 调用
CreateArrayFromList(nameList)
得到终的结果
步:将参数转换成Object(ToObject(O)
)
因为Object.keys内部会调用ToObject(O)
方法,所以它不只是可以接受对象参数,还可以接受其它类型的参数,下面这张表就是ToObject
根据不同类型的值转成Object的映射:
参数类型 | 结果 |
---|---|
Undefined | 抛出TypeError |
Null | 抛出TypeError |
Number | 返回一个新的 Number 对象 |
String | 返回一个新的 String 对象 |
Boolean | 返回一个新的 Boolean 对象 |
Symbol | 返回一个新的 Symbol 对象 |
Object | 直接将Object返回 |
相关文章