
本文介绍如何正确比较具有嵌套数组和对象的 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 同步逻辑。










