怎样用JavaScript实现深比较?

来源:Python编程网作者:大卫头衔:程序员
导读:本期聚焦于小伙伴创作的《怎样用JavaScript实现深比较?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《怎样用JavaScript实现深比较?》有用,将其分享出去将是对创作者最好的鼓励。

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

怎样用JavaScript实现深比较?

深比较的核心实现思路

实现深比较需要先明确几个关键步骤:首先判断两个值的类型,区分基本数据类型和引用数据类型;如果是基本数据类型,直接比较值是否相等;如果是引用数据类型,需要判断是数组还是普通对象,再分别遍历其元素或属性进行对比;同时要处理两个值都为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

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。