
本文详解如何正确比较含数组、对象等嵌套结构的 javascript 对象,避免因引用类型误判导致的“假差异”,并实现仅返回真正值不同的键及其新值。
在 JavaScript 中,直接使用 === 或 == 比较对象或数组时,实际比较的是内存引用地址,而非内容本身。这意味着即使两个数组或对象拥有完全相同的结构与值,只要它们是不同实例(例如两次 [] 或 {} 创建),比较结果就为 false。这正是原问题中 imagens 和 lotes 被错误标记为“不同”的根本原因——它们是引用类型,而原始函数未做深度值比对。
要解决该问题,需为嵌套结构(尤其是数组中的对象)实现浅层深度比较逻辑。以下是一个轻量、可复用的解决方案,聚焦于常见场景(对象字面量 + 数组 + 基础值类型),不依赖外部库,兼顾可读性与实用性:
✅ 核心思路
- 遍历旧对象的所有键;
- 对每个键,判断新旧值是否“值相等”:
- 若非数组,直接用 === 比较(适用于 string、number、boolean、null、undefined);
- 若为数组,则调用自定义 isObjArrayValueEqual() 进行逐项对象属性级比对;
- 仅当确认值不同时,才将键与新值写入差异对象。
✅ 关键工具函数:isObjArrayValueEqual
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 a = arr1[i], b = arr2[i];
// 要求同为非 null 对象
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;
const keysA = Object.keys(a), keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
for (const key of keysA) {
if (a[key] !== b[key]) return false; // 仅支持基础类型属性值(如 string/number/boolean)
}
}
return true;
}⚠️ 注意:此函数假设数组中每个元素均为扁平对象(无深层嵌套)。若需支持任意深度嵌套,应升级为递归深度比较(可借助 JSON.stringify() 简单场景,或使用 Lodash 的 isEqual)。
✅ 改进后的差异提取函数:getNew
const getNew = (newObj, oldObj) => {
// 边界处理:旧对象为空,直接返回新对象
if (Object.keys(oldObj).length === 0 && Object.keys(newObj).length > 0) {
return newObj;
}
const diff = {};
for (const key in oldObj) {
// 跳过原型链属性
if (!oldObj.hasOwnProperty(key)) continue;
const oldValue = oldObj[key];
const newValue = newObj[key];
// 新值不存在或类型不匹配 → 视为变更
if (newValue === undefined) continue;
// 基础类型直接比较;数组走深度比对
if (Array.isArray(newValue)) {
if (!isObjArrayValueEqual(newValue, oldValue)) {
diff[key] = newValue;
}
} else if (newValue !== oldValue) {
diff[key] = newValue;
}
}
return diff; // 始终返回 diff 对象(可能为空 {})
};✅ 使用示例与验证
const objA = { /* 原始对象(含 imagens/lotes 数组)*/ };
const objB = { ...objA }; // 浅拷贝 → imagens/lotes 引用相同 → 比较应无差异
console.log(getNew(objA, objB)); // → {}
const objC = { ...objA, imagens: [{ extension: "jpeg", url: "https://..." }] }; // 内容相同但新数组实例
console.log(getNew(objA, objC)); // → {} (✅ 正确:值相同)
const objD = { ...objA, imagens: [{ extension: "png", url: "https://..." }] }; // extension 不同
console.log(getNew(objA, objD)); // → { imagens: [...] } (✅ 正确捕获差异)✅ 总结与建议
- 永远警惕引用类型:[], {}, new Date() 等在 === 下仅比较地址;
- 按需选择深度策略:本方案针对“对象数组”做了定制化浅比较;更复杂结构建议引入成熟工具(如 Lodash _.isEqual 或 fast-deep-equal);
- 生产环境增强点:添加 typeof 安全检查、支持 null/undefined 显式处理、兼容 Map/Set 等高级类型;
- 性能提示:频繁调用时,可缓存 Object.keys() 结果或使用 for...of + entries() 提升遍历效率。
通过以上改造,你将获得一个健壮、可预测的对象差异检测工具,精准定位需更新的字段,彻底规避引用误判陷阱。










