Object.keys() 返回的键天然不包含不可枚举的系统属性(如__proto__、constructor、toString等),因此对其结果过滤这些键是冗余操作;真正需过滤的是用户自定义的同名键或getOwnPropertyNames/外部输入中的污染键。

直接过滤掉 Object.keys() 返回的系统内建保留属性(如 __proto__、constructor、toString 等)并不推荐,因为这些键名本身**不会出现在 Object.keys() 的结果中**——它们是不可枚举属性。
真正需要关注的是:你可能误以为某些“保留键”会出现在 Object.keys() 中,或实际想过滤的是**用户意外添加的、命名类似内建属性的自定义键**(比如手动设了 obj.toString = 'custom'),又或者你在处理 Object.getOwnPropertyNames() 或 Reflect.ownKeys() 这类能返回不可枚举属性的方法结果。
明确哪些键根本不会被 Object.keys() 返回
Object.keys() 只返回对象自身、可枚举(enumerable: true)的字符串键。以下常见“系统属性”默认不可枚举,因此天然被排除:
-
__proto__(访问器属性,不可枚举) -
constructor(在原型上,非自身;即使自身有,也通常不可枚举) -
toString、valueOf、hasOwnProperty等原型方法(在Object.prototype上,不可枚举且非自身)
所以,对 Object.keys(obj) 的结果做“剔除系统保留键”这一步,绝大多数情况下是冗余操作,反而增加开销。
如果真要过滤(例如处理 getOwnPropertyNames 或来自 JSON 的污染数据)
假设你用的是 Object.getOwnPropertyNames(),或接收了外部输入(如反序列化后的对象),其中可能混入了命名冲突的键(如 "toString"、"__defineGetter__"),这时可建立一个轻量白名单/黑名单:
- 优先用白名单:只保留你业务明确需要的键名(更安全)
- 若必须黑名单,可用预编译的 Set 提升性能:
const BUILTIN_KEYS = new Set(['__proto__', 'constructor', 'toString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable']); - 过滤时用
.filter(key => !BUILTIN_KEYS.has(key)),避免每次调用都重复创建数组或正则
避免误判:区分“原型链上的方法”和“对象自身的同名键”
一个对象自身完全可以拥有 toString 键(例如 { toString: 'user-123' })。此时它是合法的自有可枚举属性,Object.keys() 会返回它。是否过滤取决于你的业务语义,而非“它看起来像内建方法”。不要仅凭名称猜测用途。
- 检查是否为函数:
typeof obj[key] === 'function'(但注意:有些内建方法被重写为字符串,不绝对) - 更稳妥的方式是结合
Object.prototype.propertyIsEnumerable.call(obj, key)判断是否为自有可枚举属性,再决定是否纳入
高性能实践建议
- 不要为
Object.keys()结果做无意义过滤 —— 它已经是最精简的可枚举键集合 - 若需进一步筛选,用
Set而非Array.includes()或正则,尤其当键名集固定且数量较多时 - 避免在循环中重复调用
Object.keys()或构造新过滤函数;提取共用逻辑,必要时 memoize - 对大量对象批量处理时,考虑用
for…in+hasOwnProperty替代多次Object.keys().filter(),减少中间数组分配


















