JavaScript无内置浅深对比API,浅层对比仅检查第一层属性值是否相等(用===),深度对比需递归处理嵌套对象和数组,并防范循环引用。

JavaScript 中没有内置的浅层或深度对象对比 API,但可以通过标准方法组合实现。关键在于理解“浅层”和“深度”的含义,并选择合适的方式避免常见陷阱(比如直接用 === 比较对象总是返回 false)。
浅层对比:只检查第一层属性值是否相等
浅层对比不递归检查嵌套对象,仅比对对象自身可枚举属性的键名和对应值(使用 Object.keys() + for...in 或 Object.entries())。值为对象时,仍用 === 判断——这意味着两个不同的对象即使结构相同也会判为不等。
- 先判断引用是否相同(
a === b),相同直接返回true - 再检查是否都是对象且非
null,类型不同则返回false - 获取双方自有可枚举属性键数组(
Object.keys(a)和Object.keys(b)),长度不同则返回false - 遍历
a的每个键,检查b是否有该键,且a[key] === b[key]
深度对比:递归检查所有嵌套层级的结构与值
深度对比需递归处理对象、数组等嵌套结构。注意不能直接递归所有值(如函数、日期、正则、Map/Set 等需特殊处理),否则会出错或逻辑错误。基础实现可先聚焦普通对象和数组。
- 同样先做引用相等、类型/空值校验
- 若两者都是对象(且非
null),再判断是否同为数组(Array.isArray())或纯对象(Object.prototype.toString.call(x) === '[object Object]'),类型不同则返回false - 获取双方所有自有属性键(包括数组索引),确保数量一致
- 对每个键,递归调用深度对比函数比较
a[key]和b[key]
实用建议与注意事项
生产环境推荐使用成熟库(如 Lodash 的 _.isEqual()),它已处理循环引用、特殊对象(Date、RegExp)、原型链、不可枚举属性等边界情况。自行实现时务必注意:
立即学习“Java免费学习笔记(深入)”;
- 避免无限递归:遇到循环引用(a.b = a)必须记录已访问对象,用
WeakMap缓存比对过的对象对 - 不要忽略
undefined与缺失属性的区别(obj.a === undefined不等于!('a' in obj)) - 慎用
JSON.stringify()做“伪深度对比”:会丢失函数、undefined、Symbol、循环引用,且键顺序敏感,不可靠
一个极简但可用的深度对比函数示例
以下代码支持普通对象、数组、基本类型,带简单循环引用防护:
function deepEqual(a, b, seen = new WeakMap()) {
if (a === b) return true;
if (a == null || b == null || typeof a !== 'object' || typeof b !== 'object') return false;
if (seen.has(a)) return seen.get(a) === b;
seen.set(a, b);
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
for (const key of keysA) {
if (!keysB.includes(key)) return false;
if (!deepEqual(a[key], b[key], seen)) return false;
}
return true;
}


















