实现对象深比较 isEqual
手写 JavaScript 对象深比较函数,递归比较两个值是否相等
问题
实现一个 isEqual 函数,递归比较两个值是否相等,支持对象、数组、基本类型等。
解答
基础版本
function isEqual(a, b) {
// 严格相等,处理基本类型和同一引用
if (a === b) return true;
// 处理 NaN(NaN !== NaN)
if (Number.isNaN(a) && Number.isNaN(b)) return true;
// 如果有一个不是对象,或者是 null,直接返回 false
if (typeof a !== 'object' || typeof b !== 'object') return false;
if (a === null || b === null) 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 (!keysB.includes(key)) return false;
if (!isEqual(a[key], b[key])) return false;
}
return true;
}
完整版本(处理循环引用)
function isEqual(a, b, visited = new WeakMap()) {
// 严格相等
if (a === b) return true;
// 处理 NaN
if (Number.isNaN(a) && Number.isNaN(b)) return true;
// 类型检查
if (typeof a !== 'object' || typeof b !== 'object') return false;
if (a === null || b === null) return false;
// 类型必须一致(区分数组和对象)
if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) {
return false;
}
// 处理循环引用
if (visited.has(a)) {
return visited.get(a) === b;
}
visited.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 (!Object.prototype.hasOwnProperty.call(b, key)) return false;
if (!isEqual(a[key], b[key], visited)) return false;
}
return true;
}
测试用例
// 基本类型
console.log(isEqual(1, 1)); // true
console.log(isEqual('a', 'a')); // true
console.log(isEqual(NaN, NaN)); // true
// 对象
console.log(isEqual({ a: 1 }, { a: 1 })); // true
console.log(isEqual({ a: 1 }, { a: 2 })); // false
console.log(isEqual({ a: { b: 1 } }, { a: { b: 1 } })); // true
// 数组
console.log(isEqual([1, 2, 3], [1, 2, 3])); // true
console.log(isEqual([1, [2]], [1, [2]])); // true
// 循环引用
const obj1 = { a: 1 };
obj1.self = obj1;
const obj2 = { a: 1 };
obj2.self = obj2;
console.log(isEqual(obj1, obj2)); // true
关键点
===处理基本类型和同一引用,NaN需要单独判断- 用
Object.prototype.toString.call()区分数组和普通对象 - 递归比较嵌套属性,先比较键数量再逐个比较
- 用
WeakMap记录已访问对象,解决循环引用问题 Object.keys()只遍历自身可枚举属性,不含原型链
目录