导读:本期聚焦于小伙伴创作的《js如何判断两个对象是否相等 深度比较对象的3种实用方法》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《js如何判断两个对象是否相等 深度比较对象的3种实用方法》有用,将其分享出去将是对创作者最好的鼓励。

在JavaScript开发过程中,判断两个对象是否相等是高频出现的操作,直接使用双等号或三等号只能判断两个变量是否指向同一个对象引用,无法对比对象内部属性的具体内容,因此需要实现深度比较的逻辑。深度比较会逐层遍历对象的所有属性,对比每个属性的值是否完全一致,包括嵌套的子对象、数组等复杂结构。

js如何判断两个对象是否相等 深度比较对象的3种实用方法

方法一:基础递归实现深度比较

最直观的深度比较方式是使用递归遍历两个对象的所有属性,先判断两个值的类型,再分情况处理。如果都是对象类型就继续递归对比,如果是基础类型就直接比较值是否相等。

function deepEqual(obj1, obj2) {
  // 先判断两个值是否全等,基础类型直接返回结果
  if (obj1 === obj2) {
    return true;
  }
  // 如果有一个不是对象或者是null,直接返回不相等
  if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) {
    return false;
  }
  // 获取两个对象的所有自身属性名
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  // 属性数量不同直接不相等
  if (keys1.length !== keys2.length) {
    return false;
  }
  // 遍历属性逐个对比
  for (let key of keys1) {
    // 判断obj2是否也有这个属性,再递归对比属性值
    if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
      return false;
    }
  }
  return true;
}

// 测试示例
const a = { name: '张三', age: 20, info: { city: '北京' } };
const b = { name: '张三', age: 20, info: { city: '北京' } };
const c = { name: '李四', age: 20 };
console.log(deepEqual(a, b)); // 输出 true
console.log(deepEqual(a, c)); // 输出 false

方法二:优化后的通用深度比较方法

基础递归方法没有处理数组、日期、正则等特殊对象类型,优化后的方法会先判断值的构造函数,对不同类型做针对性处理,同时排除原型链的干扰,适用性更强。

function deepEqualOptimized(val1, val2) {
  // 基础类型全等直接返回
  if (val1 === val2) return true;
  // 判断是否为对象类型,排除null
  if (typeof val1 !== 'object' || val1 === null || typeof val2 !== 'object' || val2 === null) {
    return false;
  }
  // 获取构造函数,处理数组、日期等特殊类型
  const constructor1 = val1.constructor;
  const constructor2 = val2.constructor;
  // 构造函数不同直接不相等
  if (constructor1 !== constructor2) return false;
  // 处理数组类型
  if (constructor1 === Array) {
    if (val1.length !== val2.length) return false;
    for (let i = 0; i < val1.length; i++) {
      if (!deepEqualOptimized(val1[i], val2[i])) return false;
    }
    return true;
  }
  // 处理日期类型,转成时间戳对比
  if (constructor1 === Date) {
    return val1.getTime() === val2.getTime();
  }
  // 处理正则类型,转成字符串对比
  if (constructor1 === RegExp) {
    return val1.toString() === val2.toString();
  }
  // 处理普通对象
  const keys1 = Object.keys(val1);
  const keys2 = Object.keys(val2);
  if (keys1.length !== keys2.length) return false;
  for (let key of keys1) {
    if (!Object.prototype.hasOwnProperty.call(val2, key)) return false;
    if (!deepEqualOptimized(val1[key], val2[key])) return false;
  }
  return true;
}

// 测试示例
const arr1 = [1, 2, { x: 3 }];
const arr2 = [1, 2, { x: 3 }];
const date1 = new Date('2024-01-01');
const date2 = new Date('2024-01-01');
console.log(deepEqualOptimized(arr1, arr2)); // 输出 true
console.log(deepEqualOptimized(date1, date2)); // 输出 true

方法三:支持循环引用的深度比较

如果被比较的对象存在循环引用,前两种方法会陷入无限递归导致栈溢出,这种方法会维护一个已遍历对象的映射表,遇到已经对比过的对象直接返回结果,避免循环递归。

function deepEqualWithCycle(val1, val2, map = new WeakMap()) {
  if (val1 === val2) return true;
  if (typeof val1 !== 'object' || val1 === null || typeof val2 !== 'object' || val2 === null) {
    return false;
  }
  // 检查是否已经对比过这两个对象,避免循环引用
  if (map.has(val1) && map.get(val1) === val2) return true;
  // 记录已对比的对象对
  map.set(val1, val2);
  const constructor1 = val1.constructor;
  const constructor2 = val2.constructor;
  if (constructor1 !== constructor2) return false;
  if (constructor1 === Array) {
    if (val1.length !== val2.length) return false;
    for (let i = 0; i < val1.length; i++) {
      if (!deepEqualWithCycle(val1[i], val2[i], map)) return false;
    }
    return true;
  }
  if (constructor1 === Date) {
    return val1.getTime() === val2.getTime();
  }
  if (constructor1 === RegExp) {
    return val1.toString() === val2.toString();
  }
  const keys1 = Object.keys(val1);
  const keys2 = Object.keys(val2);
  if (keys1.length !== keys2.length) return false;
  for (let key of keys1) {
    if (!Object.prototype.hasOwnProperty.call(val2, key)) return false;
    if (!deepEqualWithCycle(val1[key], val2[key], map)) return false;
  }
  return true;
}

// 测试示例:存在循环引用的对象
const objA = { name: '测试' };
const objB = { name: '测试' };
objA.self = objA;
objB.self = objB;
console.log(deepEqualWithCycle(objA, objB)); // 输出 true

三种方法的适用场景总结

可以根据实际需求选择合适的方法,以下是三种方法的特性对比:

方法名称优势不足适用场景
基础递归实现逻辑简单,容易理解不支持数组、日期等特殊类型,无法处理循环引用仅对比普通纯对象,无复杂类型的简单场景
优化后的通用方法支持数组、日期、正则等常见特殊类型,适用性广无法处理循环引用的情况大部分常规开发场景,对象无循环引用
支持循环引用的方法可以处理存在循环引用的复杂对象逻辑相对复杂,需要额外维护映射表对象结构复杂,可能存在循环引用的场景

在实际开发中,如果项目中已经引入了工具库,也可以直接使用工具库提供的方法,比如lodash的isEqual方法,其底层实现已经处理了各种边界情况,稳定性和适用性都更好。如果只需要轻量级的实现,可以根据上述三种方法按需选择。

js深度比较对象对象相等判断递归遍历修改时间:2026-06-29 01:30:44

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