object.hasown 是 es2022 引入的安全判断对象自有属性的方法,能精准识别新增、删除及未变属性,避免原型链干扰和 hasownproperty 被重写风险,支持 symbol 键但不接受 null/undefined 输入。

Object.hasOwn 是 ES2022 引入的原生方法,用于安全判断对象自身是否拥有某个属性(不查找原型链)。它比 in 操作符更精确,也比 obj.hasOwnProperty() 更可靠(避免对象自身重写了 hasOwnProperty 方法)。在做对象状态快照比对时,它能帮你准确识别“新增”“删除”和“未变化”的自有属性,避免原型干扰。
只比自有属性,避开原型污染风险
快照比对的核心是关注对象“自己定义”的状态,而非继承来的属性。比如一个从 class 实例化出来的对象,其方法通常在原型上,而数据字段在实例自身。用 Object.hasOwn 可以严格限定范围:
- 对比前先用 Object.keys(oldObj) 和 Object.keys(newObj) 获取所有自有可枚举键,但它们无法区分属性是否存在但值为 undefined 的情况;
- Object.hasOwn(obj, key) 能明确回答“这个 key 是不是 obj 自己声明的”,哪怕值是 undefined、null 或 false,只要属性存在就返回 true;
- 这样就能排除 Object.prototype 上的 toString、hasOwnProperty 等干扰项,也不会被恶意重写的 hasOwnProperty 方法影响。
识别属性增删,而不是仅看值变化
状态比对不只是检查值是否不同,更要发现结构变化。例如配置对象中某字段被彻底删除,或新增了一个开关字段,这类变更往往比值变化更重要:
- 遍历旧快照的所有自有属性:Object.keys(oldState).forEach(key => { if (!Object.hasOwn(newState, key)) console.log(`已删除: ${key}`); });
- 遍历新快照的所有自有属性:Object.keys(newState).forEach(key => { if (!Object.hasOwn(oldState, key)) console.log(`已新增: ${key}`); });
- 对交集属性再比较值(注意:浅比较即可,深比较需额外处理);
- 这样就能分离出“结构变更”和“内容变更”,便于做差异化更新或日志记录。
配合 Object.getOwnPropertyDescriptors 做完整快照
如果需要捕获不可枚举属性(如通过 Object.defineProperty 设置的 { enumerable: false } 属性),仅靠 Object.hasOwn 不够——它只判断存在性,不读取描述符。这时可以组合使用:
- 用 Object.getOwnPropertyNames(obj) 获取所有自有属性名(含不可枚举);
- 对每个 name 调用 Object.hasOwn(obj, name) 确认它是“自有”的(虽然 getOwnPropertyNames 已保证这点,但可统一逻辑);
- 再用 Object.getOwnPropertyDescriptors(obj) 提取完整元信息(value、writable、enumerable、configurable),适合做深度快照存档;
- 后续比对时,先用 Object.hasOwn 判断属性是否存在,再按 descriptor 内容逐项比对,确保不可枚举字段也被纳入差异分析。
注意边界:Symbol 键和 null/undefined 输入
Object.hasOwn 支持 Symbol 类型的键,这是它优于 in 操作符的一点。但要注意输入安全:
- 传入 null 或 undefined 会直接抛 TypeError,比 hasOwnProperty 的静默失败更严格;
- 因此做快照比对前,建议先做类型校验:if (oldState && newState && typeof oldState === 'object' && typeof newState === 'object') { /* 安全执行 */ };
- Symbol 键会被正常识别,比如 const sym = Symbol('id'); obj[sym] = 1; Object.hasOwn(obj, sym) 返回 true;
- 但 JSON 序列化会忽略 Symbol 键,若快照要持久化,需单独处理 Symbol 属性的序列化逻辑。











