标准答案
- for...in:遍历对象自身和原型链上的可枚举字符串属性。遍历普通对象时通常要配合 Object.hasOwn 过滤继承属性,不推荐用它遍历数组。
- Object.keys、getOwnPropertyNames、Reflect.ownKeys:Object.keys 只返回自身可枚举字符串属性;Object.getOwnPropertyNames 返回自身字符串属性,包括不可枚举属性;Reflect.ownKeys 返回自身所有属性键,包括字符串键和 Symbol 键。
- for...of:遍历可迭代对象的迭代值,例如数组、字符串、Map、Set、NodeList。普通对象默认没有迭代器,不能直接 for...of。
- 选择方式:遍历普通对象自身业务字段,用 Object.keys 或 Object.entries;需要包含 Symbol 或不可枚举键,用 Reflect.ownKeys;遍历数组、Map、Set 的值,用 for...of。
题目解析
这里的核心是区分两条线:一条是“属性枚举”,一条是“迭代协议”。for...in、Object.keys、Reflect.ownKeys 关注对象属性;for...of 关注对象是否实现了可迭代协议。
for...in 会遍历原型链上的可枚举字符串属性,这是它最容易出错的地方。真实业务里遍历普通对象更常用 Object.keys/Object.entries,因为它们只看自身可枚举属性,结果更可控。
Reflect.ownKeys 的覆盖面最完整,会包含自身字符串键和 Symbol 键,也包含不可枚举属性。它适合元编程、工具库、复制对象属性等场景,但普通业务列表渲染一般不需要这么宽的范围。
对象属性顺序规则可以作为补充,但不要把顺序规则当核心。真正要先判断的是“要遍历什么”:自有可枚举属性、所有自有键,还是可迭代值。
代码示例
下面例子能说明 for...in 会看到继承的可枚举属性,而 Object.keys 只看自身可枚举属性。
JavaScript
const proto = { inherited: true }
const obj = Object.create(proto)
obj.name = 'Alice'
Object.defineProperty(obj, 'hidden', {
value: 1,
enumerable: false
})
for (const key in obj) {
console.log(key) // name, inherited
}
Object.keys(obj) // ['name']
Reflect.ownKeys(obj) // ['name', 'hidden']常见误区
- 用 for...in 遍历数组,可能遍历到自定义属性或原型属性。
- 误以为普通对象默认可以 for...of。
- 忘记 Symbol 键不会出现在 Object.keys 里。