
本文详解如何正确比较含嵌套数组/对象的 javascript 数据结构,避免因引用相等性导致的误判,提供可复用的深度值比较函数与差异提取逻辑。
在 JavaScript 中,直接使用 === 或 == 比较对象或数组时,实际比对的是内存引用地址,而非内容本身。这意味着即使两个数组结构完全一致(如 [{a: 1}] 和 [{a: 1}]),只要它们是不同实例,比较结果即为 false。这正是原问题中 imagens 和 lotes 被错误标记为“不同”的根本原因——它们是引用类型,而原始函数未做深层值比较。
要精准识别真实的数据变更,必须实现按值比较(value-based comparison),尤其针对嵌套的数组与对象。以下是一个生产就绪的解决方案,包含两个核心部分:
- isObjArrayValueEqual:专用于比较元素全为普通对象的数组(支持多层级嵌套对象,但本例暂不递归处理嵌套对象内的嵌套对象;如需完整深比较,建议引入 lodash.isEqual 或自行扩展递归逻辑);
- 增强版 getNew:在键级差异检测中,对数组字段调用上述函数,其余类型仍使用严格相等判断。
function isObjArrayValueEqual(arr1, arr2) {
if (!Array.isArray(arr1) || !Array.isArray(arr2)) return false;
if (arr1.length !== arr2.length) return false;
for (let i = 0; i < arr1.length; i++) {
const obj1 = arr1[i];
const obj2 = arr2[i];
// 确保两者均为非 null 对象
if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 === null || obj2 === null) {
return false;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
// 逐属性比对(仅支持一层对象;若需深度嵌套,请递归调用 isEqual)
for (const key of keys1) {
if (obj1[key] !== obj2[key]) return false;
}
}
return true;
}
const getNew = (newObj, oldObj) => {
// 边界处理:oldObj 为空时直接返回 newObj
if (Object.keys(oldObj).length === 0 && Object.keys(newObj).length > 0) {
return newObj;
}
const diff = {};
for (const key in oldObj) {
// 仅当 newObj 存在该 key 且值不等时进入判断
if (!(key in newObj)) continue;
if (oldObj[key] !== newObj[key]) {
// 对数组类型启用值比较
if (Array.isArray(oldObj[key]) && Array.isArray(newObj[key])) {
if (!isObjArrayValueEqual(newObj[key], oldObj[key])) {
diff[key] = newObj[key];
}
} else {
// 基本类型、null、Date、RegExp 等需额外处理;此处默认按值比较(注意:Date 需用 getTime())
diff[key] = newObj[key];
}
}
}
return diff; // 统一返回 diff 对象(空对象表示无差异)
};✅ 使用示例:
const objA = {
nome: "Lotes",
lotes: [{ lote: "LOte0", loteQtd: "8" }]
};
const objB = {
nome: "Lotes",
lotes: [{ lote: "LOte0", loteQtd: "8" }]
};
console.log(getNew(objA, objB)); // → {}(无差异)
const objC = {
nome: "Lotes",
lotes: [{ lote: "LOte0", loteQtd: "9" }]
};
console.log(getNew(objA, objC)); // → { lotes: [...] }⚠️ 注意事项:
立即学习“Java免费学习笔记(深入)”;
- 当前 isObjArrayValueEqual 仅支持数组内纯对象(不含函数、undefined、NaN、Symbol、Date、正则等特殊值)。若数据含这些类型,需扩展比较逻辑(例如:Date 用 getTime(),NaN 用 Number.isNaN() 判断)。
- 如需完整深比较(包括对象内嵌套对象、Set/Map 等),强烈推荐使用成熟库:
npm install lodash.isequal
import isEqual from 'lodash.isequal'; // 替换 isObjArrayValueEqual 判定为:isEqual(newObj[key], oldObj[key])
- 原始函数末尾 return oldObj 易引发歧义(应返回 diff 或 null),已统一改为返回 diff(空对象即无变化),符合“差异即变更”的语义直觉。
掌握引用与值的差异本质,并辅以针对性的比较策略,才能构建出鲁棒的对象差异分析工具——这是状态管理、表单脏检查、API 增量更新等场景的关键基础能力。










