在JavaScript中,基本数据类型可以直接用严格等于号比较,但是对象、数组这类引用数据类型,即使内容完全一致,直接比较也会返回false,这时候就需要通过深比较来判断两个值是否真正相等。深比较会递归遍历所有层级的数据,逐层对比内容,而不是仅仅比较引用地址。

深比较的核心实现思路
实现深比较需要先明确几个关键步骤:首先判断两个值的类型,区分基本数据类型和引用数据类型;如果是基本数据类型,直接比较值是否相等;如果是引用数据类型,需要判断是数组还是普通对象,再分别遍历其元素或属性进行对比;同时要处理两个值都为null的特殊情况,避免后续判断出错。
数据类型判断辅助函数
首先需要一个函数来判断值的类型,方便后续区分不同的数据类型:
// 判断值的类型,返回对应的类型字符串
function getType(value) {
// 处理null的特殊情况,typeof null返回object,需要单独判断
if (value === null) {
return 'null';
}
// 处理数组的情况,Array.isArray比instanceof更准确
if (Array.isArray(value)) {
return 'array';
}
// 其他情况用typeof判断
const type = typeof value;
// 如果是对象类型,返回object
if (type === 'object') {
return 'object';
}
return type;
}基础深比较实现
基于类型判断,我们可以实现一个基础的深比较函数,处理基本类型和普通的对象、数组对比:
function deepCompare(a, b) {
// 先获取两个值的类型
const typeA = getType(a);
const typeB = getType(b);
// 类型不同直接返回false
if (typeA !== typeB) {
return false;
}
// 处理基本数据类型,直接比较值
if (typeA === 'string' || typeA === 'number' || typeA === 'boolean' || typeA === 'undefined' || typeA === 'symbol' || typeA === 'bigint' || typeA === 'function') {
// 注意NaN的情况,NaN不等于自身,需要单独处理
if (Number.isNaN(a) && Number.isNaN(b)) {
return true;
}
return a === b;
}
// 处理null的情况
if (typeA === 'null') {
return a === b;
}
// 处理数组类型
if (typeA === 'array') {
// 数组长度不同直接返回false
if (a.length !== b.length) {
return false;
}
// 遍历数组每个元素递归比较
for (let i = 0; i < a.length; i++) {
if (!deepCompare(a[i], b[i])) {
return false;
}
}
return true;
}
// 处理普通对象类型
if (typeA === 'object') {
// 获取两个对象的所有自身属性名
const keysA = Object.keys(a);
const keysB = Object.keys(b);
// 属性数量不同直接返回false
if (keysA.length !== keysB.length) {
return false;
}
// 遍历属性,递归比较每个属性的值
for (const key of keysA) {
// 如果b中没有当前属性,返回false
if (!Object.prototype.hasOwnProperty.call(b, key)) {
return false;
}
// 递归比较属性值
if (!deepCompare(a[key], b[key])) {
return false;
}
}
return true;
}
// 其他未处理的类型默认返回false
return false;
}处理循环引用问题
上面的基础实现没有处理循环引用的情况,如果两个对象互相引用,递归会比较陷入无限循环,导致栈溢出。我们可以通过一个Map来记录已经比较过的对象,避免重复比较:
function deepCompareWithCycle(a, b, visited = new Map()) {
// 如果a和b都是引用类型,先检查是否已经比较过
if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) {
// 检查a是否已经在visited中
if (visited.has(a)) {
// 如果a已经比较过,判断对应的b是否和当前b一致
return visited.get(a) === b;
}
// 将a和b的对应关系存入visited
visited.set(a, b);
}
// 后续比较逻辑和基础实现一致
const typeA = getType(a);
const typeB = getType(b);
if (typeA !== typeB) {
return false;
}
if (typeA === 'string' || typeA === 'number' || typeA === 'boolean' || typeA === 'undefined' || typeA === 'symbol' || typeA === 'bigint' || typeA === 'function') {
if (Number.isNaN(a) && Number.isNaN(b)) {
return true;
}
return a === b;
}
if (typeA === 'null') {
return a === b;
}
if (typeA === 'array') {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!deepCompareWithCycle(a[i], b[i], visited)) {
return false;
}
}
return true;
}
if (typeA === 'object') {
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 (!deepCompareWithCycle(a[key], b[key], visited)) {
return false;
}
}
return true;
}
return false;
}使用示例
我们可以通过几个例子测试深比较的效果:
// 测试基本使用
const obj1 = { name: 'test', age: 18, list: [1, 2, 3] };
const obj2 = { name: 'test', age: 18, list: [1, 2, 3] };
const obj3 = { name: 'test', age: 20, list: [1, 2, 3] };
console.log(deepCompare(obj1, obj2)); // 输出true
console.log(deepCompare(obj1, obj3)); // 输出false
// 测试循环引用
const a = {};
const b = {};
a.self = a;
b.self = b;
console.log(deepCompareWithCycle(a, b)); // 输出true
// 测试数组
const arr1 = [1, { x: 2 }, [3, 4]];
const arr2 = [1, { x: 2 }, [3, 4]];
console.log(deepCompare(arr1, arr2)); // 输出true总结
实现JavaScript深比较的核心是先区分数据类型,再针对不同数据类型做对应的对比逻辑,基础场景需要处理基本类型、数组、普通对象的递归对比,额外场景还需要处理循环引用、NaN特殊值等问题。实际开发中可以根据需求选择基础版本或者带循环引用处理的版本,只要注意递归的终止条件和遍历逻辑正确,就能实现可靠的深比较功能。
JavaScript深比较递归对象比较数据类型判断修改时间:2026-06-05 02:13:51