对象遍历

Object.keys()

这个方法获取的是「自身」「可枚举」 的属性的key, 不可枚举及从原型继承来的属性的 key 不可获得,返回值为 key 组成的数组。

Object.getOwnPropertyNames()

这个方法获取的是自身属性的 key, 包括可枚举和不可枚举属性的 key,返回值为 key 组成的数组。

for in

遍历自身及原型链上继承来的可枚举属性

上面介绍的这几项无法获取到 Symbol 值的 key,但可以通过 Object.getOwnPropertySymbols() 获取自身的 Symbol 值的key,这里不考虑 Symbol 的情况。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const testObj = {
1: 'one',
two: 2,
three: 3,
four: 4,
5: 'five',
};
Object.defineProperty(testObj, '6', {
value: 'six',
enumerable: false,
});
Object.prototype.seven = 7;
console.log('---- Object.keys ----');
console.log(Object.keys(testObj));

console.log('---- Object.getOwnPropertyNames ----');
console.log(Object.getOwnPropertyNames(testObj));

console.log('---- for in ----');
for(let key in testObj) console.log(key);

打印结果如下:
image.png

分析并发现问题

testObj 的属性中, key 值为 ‘6’ 的为不可枚举属性,key 值为 ‘seven’ 的为原型链上的属性。Object.keys 和 Object.getOwnPropertyNames 返回的都是自身的属性,区别在于 getOwnPropertyNames 返回结果中包含不可枚举的属性,for in 遍历自身及原型链上可枚举的属性。
从打印的结果我发现几个问题:

  • key 的顺序为什么不是创建的顺序
  • for … in 如何只遍历自身属性的 key
  • 如何得到对象自身不可枚举的 key

1. key 的顺序问题

大多数浏览器的新版本按照这样的方式处理: 将整数型的key排在前面,不是整数型的key按照创建的顺序排(整数型指变为整数再从整数变回来,改变前后完全相同的数), 例如 “+1” -> 1 -> ‘1’, 则 +1 不是整数型key).
我们可以改下上面的 testObj,再次进行测试。

1
2
3
4
5
6
7
8
9
10
11
const testObj = {
1: 'one',
two: 2,
three: 3,
four: 4,
5: 'five',
};
console.log(Object.keys(testObj));
delete testObj['two']
testObj['two'] = '2'; // 新创建
console.log(Object.keys(testObj));

image.png
但在一些老的浏览器并不是按照这样的方式处理的,所以不同浏览器存在差异,如果想对顺序有特别的要求,建议还是对返回的存储key的数组进行处理后,使其按特定顺序输出。

2. for … in 如何只遍历自身属性的 key

可以使用 *hasOwnProperty *进行判断过滤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const testObj = {
1: 'one',
two: 2,
three: 3,
four: 4,
5: 'five',
};
Object.prototype.seven = 7;
console.log('---- for in ----');
for(let key in testObj) {
if (testObj.hasOwnProperty(key)) {
console.log(key);
}
}

image.png

3.如何得到对象自身不可枚举的 key

不考虑 Symbol 的key,Object.getOwnPropertyNames() 得到的 key 去掉 Object.keys() 得到的 key 就是不可枚举的 key。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const testObj = {
1: 'one',
two: 2,
three: 3,
four: 4,
5: 'five',
};
Object.defineProperty(testObj, '6', {
value: 'six',
enumerable: false,
});
console.log(testObj);
const allKeys = Object.getOwnPropertyNames(testObj);
const enumerableKeys = Object.keys(testObj);
const unEnumerableKeys = allKeys.filter(key => enumerableKeys.indexOf(key) === -1);
console.log('unEnumerableKeys:', unEnumerableKeys);

这里 chrome 浏览器有个奇怪的 bug, 去掉第12行的打印在 Chrome 74.0.3729.169(正式版本) (64 位)通过 getOwnPropertyNames 无法得到不可枚举的key, safari 测试正常。
image.png