比较两个嵌套对象并精准提取值不同的键路径

7次阅读

比较两个嵌套对象并精准提取值不同的键路径

本文介绍如何正确比较具有嵌套数组和对象javascript 数据结构,避免因引用类型导致的误判,并提供可复用的深度差异检测函数。

javaScript 中,对象和数组属于引用类型:即使两个数组内容完全相同,只要它们是不同实例(即内存地址不同),使用 === 或 == 比较时结果恒为 false。这正是原问题中 imagens 和 lotes 被错误标记为“不同”的根本原因——你的比较逻辑未进行值相等性(deep equality)判断,而是直接依赖浅层引用比较。

以下是一个健壮、可扩展的解决方案,专为含嵌套对象数组的场景设计:

✅ 核心思路

  • 对基础类型(字符串、数字、布尔、NULLundefined)直接使用 ===;
  • 对数组,逐项递归比对每个元素(支持嵌套对象);
  • 对普通对象,先校验键名集合是否一致,再逐键比对值;
  • 避免修改原始数据,返回最小差异键值对集合。

✅ 改进后的 getDiffKeys 函数(推荐命名更语义化)

function deepEqual(a, b) {   // 类型不同直接不等   if (typeof a !== typeof b) return false;   if (a === b) return true; // 处理 null, undefined, 基础值    // 数组:长度 + 每项 deepEqual   if (Array.isArray(a) && Array.isArray(b)) {     if (a.length !== b.length) return false;     return a.every((item, i) => deepEqual(item, b[i]));   }    // 普通对象:键名一致 + 所有键值 deepEqual   if (typeof a === 'object' && typeof b === 'object') {     const keysA = Object.keys(a);     const keysB = Object.keys(b);     if (keysA.length !== keysB.length) return false;     if (!keysA.every(key => keysB.includes(key))) return false;     return keysA.every(key => deepEqual(a[key], b[key]));   }    return false; }  function getDiffKeys(newObj, oldObj) {   // 空旧对象 → 全量更新   if (Object.keys(oldObj).length === 0 && Object.keys(newObj).length > 0) {     return { ...newObj };   }    const diff = {};   for (const key in oldObj) {     // 仅当新对象存在该键且值不等时才记录     if (newObj.hasOwnProperty(key) && !deepEqual(oldObj[key], newObj[key])) {       diff[key] = newObj[key];     }   }   return diff; }

✅ 使用示例

const obj1 = {   crm: "",   dateReceita: "2023-07-27T03:00:00.000Z",   imagens: [{ extension: "jpeg", url: "https://..." }],   lotes: [     { lote: "LOte0", loteQtd: "8" },     { lote: "Lote 2", loteQtd: "2" }   ],   nome: "Lotes",   produto: "Dem",   quantidade: 10,   tipo: "Branca",   vendida: true };  const obj2 = { ...obj1 }; // 完全相同的副本 → 无差异 console.log(getDiffKeys(obj1, obj2)); // {}  // 修改一个嵌套字段 const obj3 = {   ...obj1,   imagens: [{ ...obj1.imagens[0], what: "wat" }] // 新增字段 }; console.log(getDiffKeys(obj1, obj3)); // → { imagens: [{ extension: "jpeg", url: "...", what: "wat" }] }

⚠️ 注意事项

  • 性能提示:deepEqual 是同步递归操作,对超大嵌套结构(如千级数组)可能影响性能;生产环境建议配合 lodash.isEqual 或 fast-deep-equal 等优化库;
  • 边界兼容:当前实现不处理 Date、regexpmap/Set 等特殊对象,如需支持,应在 deepEqual 中补充类型判断分支;
  • 键缺失处理:本方案默认只对比 oldObj 中存在的键;若需检测新增键(如 newObj 有而 oldObj 无),可扩展逻辑遍历 newObj 并检查 !oldObj.hasOwnProperty(key);
  • 返回格式:函数返回的是「差异键及其新值」的对象,符合前端 PATCH 更新或后端校验场景需求。

通过将引用比较升级为深度值比较,你就能真正识别出哪些嵌套字段发生了实质变更,从而安全、精准地驱动 ui 更新、表单校验或 API 同步逻辑。

text=ZqhQzanResources