
本文介绍如何正确比较具有嵌套数组和对象的 javascript 数据结构,避免因引用类型导致的误判,并提供可复用的深度差异检测函数。
在 javaScript 中,对象和数组属于引用类型:即使两个数组内容完全相同,只要它们是不同实例(即内存地址不同),使用 === 或 == 比较时结果恒为 false。这正是原问题中 imagens 和 lotes 被错误标记为“不同”的根本原因——你的比较逻辑未进行值相等性(deep equality)判断,而是直接依赖浅层引用比较。
以下是一个健壮、可扩展的解决方案,专为含嵌套对象数组的场景设计:
✅ 核心思路
- 对基础类型(字符串、数字、布尔、NULL、undefined)直接使用 ===;
- 对数组,逐项递归比对每个元素(支持嵌套对象);
- 对普通对象,先校验键名集合是否一致,再逐键比对值;
- 避免修改原始数据,返回最小差异键值对集合。
✅ 改进后的 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、regexp、map/Set 等特殊对象,如需支持,应在 deepEqual 中补充类型判断分支;
- 键缺失处理:本方案默认只对比 oldObj 中存在的键;若需检测新增键(如 newObj 有而 oldObj 无),可扩展逻辑遍历 newObj 并检查 !oldObj.hasOwnProperty(key);
- 返回格式:函数返回的是「差异键及其新值」的对象,符合前端 PATCH 更新或后端校验场景需求。
通过将引用比较升级为深度值比较,你就能真正识别出哪些嵌套字段发生了实质变更,从而安全、精准地驱动 ui 更新、表单校验或 API 同步逻辑。