JavaScript对象属性遍历需按需求选择API:for...in遍历可枚举的自有+继承属性但需hasOwnProperty过滤;Object.keys()等仅返回自有可枚举字符串键;getOwnPropertyNames()和getOwnPropertySymbols()分别获取所有自有字符串键和Symbol键;Reflect.ownKeys()最完整,涵盖所有自有键。

JavaScript 对象的属性遍历不能靠 for...in 一股脑全用,得看你要不要继承属性、要不要不可枚举属性、要不要 Symbol 类型键——不同方法返回结果差异很大,一不留神就漏掉或误读。
for...in 循环:只遍历可枚举的自有 + 继承属性
它会顺着原型链往上找所有 可枚举 属性(包括从 Object.prototype 继承的 toString、hasOwnProperty 等),但跳过不可枚举属性和 Symbol 键。
- 必须配合
obj.hasOwnProperty(key)过滤,否则容易把原型上的方法当对象自身属性处理 - 不保证遍历顺序(ES2015 后对数字键有排序,但字符串键仍无序)
- 完全忽略 Symbol 类型的键
const obj = { a: 1 };
Object.defineProperty(obj, 'b', { value: 2, enumerable: false });
obj.c = 3;
for (const key in obj) {
console.log(key); // 输出 'a' 和 'c',但不会输出 'b'
}
Object.keys() / Object.values() / Object.entries():只返回自有可枚举属性
这三个方法都只返回对象自身的、可枚举的字符串键属性,不涉及原型链,也不包含 Symbol 键。
-
Object.keys()返回字符串键数组,最常用;Object.values()和Object.entries()分别对应值和[key, value]对 - 它们的顺序是确定的:先按数字键升序,再按插入顺序排列字符串键(ES2015+)
- 性能比
for...in+hasOwnProperty略好,因为不用反复查原型
const obj = { z: 1, a: 2, 10: 'ten', 2: 'two' };
console.log(Object.keys(obj)); // ['2', '10', 'z', 'a'] —— 数字键排前面,按数值升序
Object.getOwnPropertyNames() 和 Object.getOwnPropertySymbols():获取所有自有属性(含不可枚举 + Symbol)
这两个方法互补:前者返回所有自有字符串键(无论是否可枚举),后者返回所有自有 Symbol 键。合起来才等于对象全部自有属性。
立即学习“Java免费学习笔记(深入)”;
-
Object.getOwnPropertyNames(obj)包含constructor、__proto__(如果被定义为自有属性)、以及手动设为enumerable: false的属性 -
Object.getOwnPropertySymbols(obj)是唯一能拿到 Symbol 键的方式(for...in、Object.keys()都看不到) - 二者都不访问原型链,纯粹“快照式”读取当前对象结构
const sym = Symbol('id');
const obj = { a: 1 };
Object.defineProperty(obj, 'hidden', { value: 2, enumerable: false });
obj[sym] = 'symbol-value';
console.log(Object.getOwnPropertyNames(obj)); // ['a', 'hidden']
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(id)]
Reflect.ownKeys():最完整的自有键列表(字符串 + Symbol)
它等价于 [...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj)],是目前唯一一个一步到位拿到对象所有自有键的方法。
- 返回数组,顺序规则同
Object.keys():数字键升序 → 字符串键插入顺序 → Symbol 键插入顺序 - 在 Proxy 中作为
ownKeystrap 的返回值标准,语义更明确 - 如果你需要完整还原对象结构(比如序列化、深克隆、diff),优先用这个
const obj = { a: 1 };
Object.defineProperty(obj, 'b', { value: 2, enumerable: false });
obj[Symbol('s')] = 'sym';
console.log(Reflect.ownKeys(obj)); // ['a', 'b', Symbol(s)]
真正麻烦的是混合场景:比如你写了个工具函数要“安全地遍历对象所有自有属性”,就得决定是否包含不可枚举属性、是否处理 Symbol、是否兼容旧环境(Reflect.ownKeys 在 IE 完全不支持)。这时候别硬塞进一个“万能遍历”,而是根据实际用途选最窄够用的那个 API——多数业务代码里,Object.keys() 已经覆盖 90% 需求;只有元编程、框架开发或调试时,才需要碰 getOwnPropertyNames 或 Reflect.ownKeys。











