JS深比较两个对象
七崽爱吃小饼干2026/03/02阅读 1
JS 实现两个对象的深比较
一个递归深比较函数,能对比两个任意类型的值(基本类型、对象、数组、嵌套对象/数组),不仅比较引用地址,还逐层比较所有属性和值是否完全一致。
完整实现代码
javascript
/**
* 深比较两个值是否完全相等(支持基本类型、对象、数组、null、undefined、循环引用)
* @param {any} obj1 - 第一个比较值
* @param {any} obj2 - 第二个比较值
* @param {WeakMap} [visited] - 内部使用,处理循环引用
* @returns {boolean} 相等返回 true,不相等返回 false
*/
function deepEqual(obj1, obj2, visited = new WeakMap()) {
// 1. 严格相等(基本类型、同一引用、null/undefined 直接判断)
if (obj1 === obj2) return true;
// 2. 只要有一个不是对象/函数,且第一步不相等,直接返回 false
if (
typeof obj1 !== 'object' || obj1 === null ||
typeof obj2 !== 'object' || obj2 === null
) {
return false;
}
// 3. 处理循环引用(防止无限递归)
if (visited.has(obj1)) {
return visited.get(obj1) === obj2;
}
visited.set(obj1, obj2);
// 4. 获取所有可枚举属性(包含自身属性,不区分 Symbol/字符串)
const keys1 = Reflect.ownKeys(obj1);
const keys2 = Reflect.ownKeys(obj2);
// 5. 属性数量不一致,直接不相等
if (keys1.length !== keys2.length) return false;
// 6. 递归比较每一个属性
for (const key of keys1) {
const val1 = obj1[key];
const val2 = obj2[key];
// 递归深比较子属性
if (!deepEqual(val1, val2, visited)) {
return false;
}
}
return true;
}
核心功能说明
这个函数支持所有场景的深比较:
- 基本类型:数字、字符串、布尔值、
null、undefined、Symbol - 引用类型:普通对象、数组、嵌套对象/数组
- 特殊处理:
- 循环引用对象(不会无限递归崩溃)
Symbol属性- 不可枚举属性
- 严格区分
0和-0、NaN和NaN
测试用例(验证效果)
javascript
// 1. 基本类型测试
console.log(deepEqual(1, 1)); // true
console.log(deepEqual('a', 'b')); // false
console.log(deepEqual(NaN, NaN)); // true
console.log(deepEqual(0, -0)); // false
// 2. 对象测试
const obj1 = { a: 1, b: { c: [2, 3] } };
const obj2 = { a: 1, b: { c: [2, 3] } };
const obj3 = { a: 1, b: { c: [2] } };
console.log(deepEqual(obj1, obj2)); // true
console.log(deepEqual(obj1, obj3)); // false
// 3. 数组测试
console.log(deepEqual([1, { a: 2 }], [1, { a: 2 }])); // true
// 4. 循环引用测试
const loopObj1 = {};
const loopObj2 = {};
loopObj1.self = loopObj1;
loopObj2.self = loopObj2;
console.log(deepEqual(loopObj1, loopObj2)); // true
关键逻辑拆解
- 第一步快速判断:用
===直接比较,基本类型、同一引用会直接返回结果 - 非对象过滤:排除非对象类型,避免递归报错
- 循环引用处理:用
WeakMap记录已比较的对象,防止无限递归 - 全属性对比:用
Reflect.ownKeys获取所有属性(包含 Symbol/不可枚举属性) - 递归比较:逐层对比子属性/子元素,直到最底层基本类型
总结
- 这个深比较函数通用且健壮,支持前端开发中所有常见场景
- 核心是递归 + 全属性遍历,同时解决了循环引用的坑
- 相比
JSON.stringify比较,能正确处理undefined、Symbol、函数、循环引用等特殊情况