JavaScript对象属性遍历需按需求选择API:for...in遍历可枚举的自有+继承属性但需hasOwnProperty过滤;Object.keys()等仅返回自有可枚举字符串键;getOwnPropertyNames()和getOwnPropertySymbols()分别获取所有自有字符串键和Symbol键;Reflect.ownKeys()最完整,涵盖所有自有键。
JavaScript 对象的属性遍历不能靠 for...in 一股脑全用,得看你要不要继承属性、要不要不可枚举属性、要不要 Symbol 类型键——不同方法返回结果差异很大,一不留神就漏掉或误读。
它会顺着原型链往上找所有 可枚举 属性(包括从 Object.prototype 继承的 toString、hasOwnProperty 等),但跳过不可枚举属性和 Symbol 键。
obj.hasOwnProperty(key) 过滤,否则容易把原型上的方法当对象自身属性处理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'
}
这三个方法都只返回对象自身的、可枚举的字符串键属性,不涉及原型链,也不包含 Symbol 键。
Object.keys() 返回字符串键数组,最常用;Object.values() 和 Object.entries() 分别对应值和 [key, value] 对for...in + hasOwnProperty 略好,因为不用反复查原型const obj = { z: 1, a: 2, 10: 'ten', 2: 'two' };
console.log(Object.keys(obj)); // ['2', '10', 'z', 'a'] —— 数字键排前面,按数值升序
这两个方法互补:前者返回所有自有字符串键(无论是否可枚举),后者返回所有自有 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)]
它等价于 [...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj)],是目前唯一一个一步到位拿到对象所有自有键的方法。
Object.keys():数字键升序 → 字符串键插入顺序 → Symbol 键插入顺序ownKeys trap 的返回值标准,语义更明确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。