JavaScript 中使用 hasOwnProperty 实现简单的对象深度对比

作者:技术拾光

时间:2026-07-26

浏览:335

hasOwnProperty仅判断对象自身属性,无法单独实现深度对比。其核心作用是过滤继承属性,配合递归和类型判断,确保只比较自有属性,避免原型链干扰。基础深度对比函数借助Object.keys和hasOwnProperty实现,for...in场景下不可省略该检查。

文章目录

深度对比中的 hasOwnProperty:能做什么,不能做什么

判断两个对象是否相等?这是一个经典问题。直接使用 hasOwnProperty 能否完成?答案是不能。它只判断对象自身是否拥有某个属性——注意,是“自身”,不涉及原型链。但如果要实现可靠的深度对比,hasOwnProperty 又是不可或缺的基础工具。关键在于,需要将其与递归、类型判断组合使用。

JavaScript 中使用 hasOwnProperty 实现简单的对象深度对比

为什么不能仅靠 hasOwnProperty?

实际上,hasOwnProperty 的职责非常单一——它只回答“这个 key 是否属于对象自身定义”。不比较值、不递归、不处理数组或嵌套对象。一个典型的反例是:两个结构完全相同的对象,但属性分别来自原型链,此时 hasOwnProperty 检查结果可能完全一致,而实际内容却截然不同。因此,不要指望它单靠自身就能完成深度对比。

hasOwnProperty 的真正角色:过滤继承属性

遍历对象时,for...in 会将原型链上的属性也一并取出——toString、valueOf 等方法都会参与进来。如果不加过滤直接对比,结果必然错误。这就是 hasOwnProperty 在深度对比中的核心价值:

  • 遍历对象所有 key 时,先用 obj.hasOwnProperty(key) 确认该 key 属于实例本身
  • 将 for...in 默认包含的原型属性全部排除
  • 避免因原型链污染或扩展导致的对比错误

简单来说,它就像一个“守门员”,只放行自有属性。

一个可用的基础深度对比函数

以下实现轻量且可读性强,支持普通对象和原始值,核心正是依靠 hasOwnProperty 保证只对比自有属性:

function deepEqual(a, b) {
  if (a === b) return true;
  if (a == null || b == null) return false;
  if (typeof a !== 'object' || typeof b !== 'object') return false;

  const keysA = Object.keys(a);
  const keysB = Object.keys(b);

  if (keysA.length !== keysB.length) return false;

  for (const key of keysA) {
    // 关键步骤:确保是自有属性,排除原型干扰
    if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) continue;
    if (!deepEqual(a[key], b[key])) return false;
  }

  return true;
}

这里有一个细节:Object.keys() 本身只返回自有可枚举属性,已经隐含了 hasOwnProperty 的逻辑,因此上述显式检查实际上是冗余的。但之所以写出来,一是为了让意图更清晰,二是为了兼容 for...in 场景——在那种情况下,缺少它就会出问题。

更稳妥的写法:兼容 for...in 场景

如果选择使用 for...in 遍历(例如需要兼容旧环境或特殊枚举控制),hasOwnProperty 就绝对不可省略:

for (let key in a) {
  if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) continue;
  if (!deepEqual(a[key], b[key])) return false;
}

另外需要提醒一点:上述实现只处理了普通对象和原始值。如果遇到数组、Date、RegExp 等特殊类型,它们会被当作普通对象递归处理,结果很可能不符合预期。在实际生产环境中,建议针对这些类型单独处理。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章