很多人会在遍历对象属性时担心“怎么把不可枚举属性过滤掉”,于是又加一层 `filter` 或去查属性描述符。实际上,如果你用的是 Object.keys(),这一步通常根本不需要做:它默认就只返回对象自身、可枚举的字符串键名。
这篇文章把几个常见对象遍历 API 的范围拆开讲清楚,并用简单示例说明什么时候可以直接用 Object.keys(),什么时候则应该改用别的接口。看完后,你可以快速判断当前场景到底要排除不可枚举属性、包含 Symbol,还是连原型链上的可枚举属性也一起处理。
Object.keys 默认就会排除不可枚举属性
Object.keys() 的行为很明确:它只返回对象自身的、且 enumerable: true 的字符串键名。因此有三类属性会被天然排除:

- 不可枚举属性
Symbol属性- 原型链上的属性
也就是说,只要你的目标是“遍历当前对象上能正常枚举出来的普通键”,直接使用它即可,不需要再额外判断一次可枚举性。
const obj = {};
Object.defineProperty(obj, 'hidden', {
value: 'secret',
enumerable: false // 不可枚举
});
obj.visible = 'ok';
console.log(Object.keys(obj)); // ['visible']
// 'hidden' 自动被排除,无需手动判断上面这段代码已经说明了关键点:hidden 虽然确实存在于对象自身上,但因为它是不可枚举属性,所以不会出现在 Object.keys(obj) 的结果里。
几个常见遍历 API 的范围差别
之所以会对 Object.keys() 产生误判,往往是因为开发中混用了多个对象遍历 API。它们看起来都像是在“取属性名”,但返回范围并不一样。
Object.getOwnPropertyNames()
这个方法返回对象自身所有字符串键名,包括不可枚举属性。它不会返回 Symbol,但会把像上例中的 hidden 也带出来。
Object.getOwnPropertySymbols()
这个方法只返回对象自身的 Symbol 键名。如果你的对象用 Symbol 存放元信息、内部标记或避免命名冲突,就需要它来补齐这部分属性。

for...in
for...in 会遍历对象自身以及原型链上的所有可枚举字符串属性。因此它的风险点不在“会不会拿到不可枚举属性”,而在于它可能把继承来的可枚举属性也一起遍历出来。
Reflect.ownKeys()
Reflect.ownKeys() 返回对象自身的所有键名,包括:
- 字符串键
Symbol键- 可枚举和不可枚举属性
如果你想拿到最完整的“自身属性清单”,它比 Object.keys() 和 Object.getOwnPropertyNames() 更全面。
遍历时再判断 enumerable,通常属于重复过滤
既然 Object.keys() 已经保证结果只包含可枚举字符串键,那么下面这种写法通常就是重复工作:
// ❌ 多此一举:Object.keys 已保证全是可枚举的
Object.keys(obj).filter(key => Object.getOwnPropertyDescriptor(obj, key).enumerable)
// ✅ 直接用即可
Object.keys(obj).forEach(key => { /* 安全使用 key */ })这类额外过滤的问题不只是“代码显得啰嗦”,还会让读代码的人误以为 Object.keys() 本身可能返回不可枚举属性,从而加深认知混乱。
因此在常规业务代码里,如果你已经选定 Object.keys(),通常就可以直接进入遍历逻辑,把精力放在属性值处理本身,而不是重复校验它的筛选结果。
需要包含哪些属性,决定你该选哪个 API
真正值得判断的,不是“拿到结果后怎么过滤”,而是“一开始该选哪个 API”。可以按目标来分:
- 要对象自身的可枚举字符串键:用
Object.keys() - 要对象自身所有字符串键,包含不可枚举属性:用
Object.getOwnPropertyNames() - 要对象自身所有
Symbol键:用Object.getOwnPropertySymbols() - 要对象自身全部键,包含字符串、
Symbol和不可枚举属性:用Reflect.ownKeys() - 要判断某个指定属性是否可枚举:用
Object.prototype.propertyIsEnumerable.call(obj, key)
换句话说,Object.keys() 适合“直接遍历当前对象公开可枚举字段”的场景;如果你的需求开始涉及内部字段、Symbol 元数据或完整反射能力,就应切换到更匹配的接口,而不是在 Object.keys() 结果上继续补救式加工。
实际使用中的判断结论
围绕“遍历时过滤不可枚举属性”这个问题,可以直接记住一个结论:只要使用 Object.keys(),不可枚举属性本来就不会出现在结果里。
多数情况下,你只需要分清两件事:
- 是否只关心对象自身属性,而不碰原型链
- 是否需要把不可枚举属性或
Symbol一起拿出来
如果答案是“只要自身、只要可枚举、只要字符串键”,那 Object.keys() 就已经足够,而且是语义最直接的选择。只有当你的需求超出这个边界时,才需要换成 Object.getOwnPropertyNames()、Object.getOwnPropertySymbols() 或 Reflect.ownKeys()。













