深入理解 Object.keys()
我们先来看看在 MDN[6] 上关于 Object.keys()
的描述:
Object.keys()
方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
emmm... 并没有直接告诉我们输出顺序是什么,不过我们可以看看上面的 Polyfill[7] 是怎么写的:
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 = [];//1.创建一个空的列表用于存放 keys
for (var prop in obj) {
if (hasOwnProperty.call(obj, prop)) result.push(prop);
}
if (hasDontEnumBug) {
for (var i=0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
}
}
return result;
}
})()
};
- 创建一个空的列表用于存放 keys
- 将所有合法的数组索引按升序的顺序存入
- 将所有字符串类型索引按属性创建时间以升序的顺序存入
- 将所有
Symbol
类型索引按属性创建时间以升序的顺序存入 - 返回 keys
这里顺便也纠正一个普遍的误区:有些回答说将所有属性为数字类型的 key 从小到大排序,其实不然,还必须要符合 「合法的数组索引」 ,也即只有正整数才行,负数或者浮点数,一律当做字符串处理。
PS:严格来说对象属性没有数字类型的,无论是数字还是字符串,都会被当做字符串来处理。
let o = {
2: "b",
3: "c",
1: "a"
}
console.log(Object.keys(o))
Object.keys(o).forEach((value, index, array) => {
console.log(value);
})
//打印
(3) ["1", "2", "3"]
1
2
3
看下这个例子:
const testObj = {}
testObj[-1] = ''
testObj[1] = ''
testObj[1.1] = ''
testObj['2'] = ''
testObj['c'] = ''
testObj['b'] = ''
testObj['a'] = ''
testObj[Symbol(1)] = ''
testObj[Symbol('a')] = ''
testObj[Symbol('b')] = ''
testObj['d'] = ''
console.log(Object.keys(testObj))
结果:
['1', '2', '-1', '1.1', 'c', 'b', 'a', 'd']
我们如果想实现对象属性是数字,并且还要按照存入的顺序,返回
将数字转换为浮点型即可