题目
实现一个深度比较两个对象是否相等的函数
信息
- 类型:问答
- 难度:⭐⭐
考点
对象比较,递归算法,类型判断,边界条件处理
快速回答
实现深度对象比较需要:
- 处理基本类型和引用类型的差异
- 递归比较嵌套对象和数组
- 特殊边界处理(日期、正则等)
- 避免循环引用导致的无限递归
- 考虑构造函数一致性和原型链
深度比较两个对象需要全面考虑JavaScript的各种数据类型和边界情况。
核心实现原理
递归遍历对象属性,分层比较:
- 首先通过
===比较基本类型 - 检查是否为null或非对象类型
- 比较构造函数和原型链
- 递归比较嵌套属性
代码示例
function deepEqual(obj1, obj2, seen = new WeakMap()) {
// 基本类型直接比较
if (obj1 === obj2) return true;
// 处理null和undefined
if (obj1 == null || obj2 == null || typeof obj1 !== 'object' || typeof obj2 !== 'object') {
return obj1 === obj2;
}
// 循环引用检测
if (seen.has(obj1) && seen.get(obj1) === obj2) return true;
seen.set(obj1, obj2);
// 比较构造函数
if (obj1.constructor !== obj2.constructor) return false;
// 特殊对象处理
if (obj1 instanceof Date) return obj1.getTime() === obj2.getTime();
if (obj1 instanceof RegExp) return obj1.toString() === obj2.toString();
// 数组比较
if (Array.isArray(obj1)) {
if (obj1.length !== obj2.length) return false;
return obj1.every((item, i) => deepEqual(item, obj2[i], seen));
}
// 对象属性比较
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
return keys1.every(key => {
return obj2.hasOwnProperty(key) &&
deepEqual(obj1[key], obj2[key], seen);
});
}最佳实践
- 使用
WeakMap跟踪比较过的对象避免循环引用 - 优先比较构造函数提高效率
- 单独处理
Date、RegExp等特殊对象 - 使用
Object.keys()确保只比较自身属性
常见错误
- 未处理循环引用导致栈溢出
- 忽略构造函数差异(如
[]与{}) - 未考虑特殊对象的时间戳比较
- 使用
for-in遍历会包含原型链属性 - 未处理
Symbol类型属性
扩展知识
- 循环引用检测:通过WeakMap存储已比较对象,键值弱引用避免内存泄漏
- Symbol属性处理:可通过
Object.getOwnPropertySymbols()补充 - 性能优化:复杂对象可先进行浅比较(
Object.is()) - 边界情况:
NaN、+0/-0需特殊处理(本例中由===处理)