如何遍历JavaScript的对象

原文:How to Iterate Over a JavaScript Object

有许多种方法可以遍历JavaScript对象的所有key和value。本文介绍3种方法和它们的优缺点。

有一个简单的JavaScript对象:

1
2
3
4
5
const obj = {
name: 'Luke Skywalker',
title: 'Jedi Knight',
age: 23
};

如何遍历key/value对,并打印“name: Luke Skywalker”、“title: Jedi Knight”和“age: 23”?现代JavaScript种有许多方法实现,本文介绍了3种不同的方法:

Object.entries()

Object.entries()函数返回一个数组,其中包含对象的key/value对。使用Object.entries()for/of循环,可以打印key/value对。

1
2
3
4
5
6
7
8
9
10
11
12
13
const obj = {
name: 'Luke Skywalker',
title: 'Jedi Knight',
age: 23
};

// 打印:
// 'name: Luke Skywalker'
// 'title: Jedi Knight'
// 'age: 23'
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`);
}

Object.entries(obj)返回的数组,其中每个都包含2个元素,第一个是key,第二个是value。这种模式在JavaScript中很常见,例如Map的constructorObject.fromEntries()

如果用Array.from()Object.entries()返回的迭代器转换为数组,数组中包含的就是对象的key/value对。

1
2
3
4
5
6
7
8
9
10
const obj = {
name: 'Luke Skywalker',
title: 'Jedi Knight',
age: 23
};

const keyValuePairs = Array.from(Object.entries(obj));
keyValuePairs[0]; // ['name', 'Luke Skywalker']
keyValuePairs[1]; // ['title', 'Jedi Knight']
keyValuePairs[2]; // ['age', 23]

Object.keys()

Object.keys()函数返回对象key的数组,而不是key/value对。可以用方括号[]获取对象的value。

1
2
3
4
5
6
7
8
9
10
11
12
13
const obj = {
name: 'Luke Skywalker',
title: 'Jedi Knight',
age: 23
};

// 打印
// 'name: Luke Skywalker'
// 'title: Jedi Knight'
// 'age: 23'
for (const key of Object.keys(obj)) {
console.log(`${key}: ${obj[key]}`);
}

有了Object.entries()为什么还要有Object.keys()?因为entry中的值是无法改变对象中的值的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const obj = {
name: 'Luke Skywalker',
title: 'Jedi Knight',
age: 23
};

// 设置`value`不能改变属性值
// 需要设置`obj[key] = 新的值`
for (let [key, value] of Object.entries(obj)) {
if (key === 'title') {
value = 'Jedi Master';
}
}
obj.title; // 'Jedi Knight'

// 可以修改!
for (const key of Object.keys(obj)) {
if (key === 'title') {
obj[key] = 'Jedi Master';
}
}

for/in

Object.keys()Object.entries()只会循环对象自身的属性。对于POJO而言,区别不大,但在遇到继承的情况,可能会有一些问题。for/in循环可以遍历对象的所有key,包括继承的key。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function JediKnight(name, age) {
this.name = name;
this.age = age;
}
// `JediKnight`类的实例会继承属性`title`
JediKnight.prototype.title = 'Jedi Knight';

const obj = new JediKnight('Luke Skywalker', 23);

// `entries`不包括`title`属性
const entries = Object.entries(obj);
entries; // [['name', 'Luke Skywalker'], ['age', '23']]

// 包括`title`因为`for/in`会遍历继承属性。
// 'name: Luke Skywalker'
// 'age: 23'
// 'title: Jedi Knight'
for (const key in obj) {
console.log(`${key}: ${obj[key]}`);
}

一般来说,应该使用Object.keys()Object.entries()遍历POJO,避免意外取到继承属性。但如果确实需要遍历继承属性,就需要用for/in了。