标题:深度比较嵌套对象并精准提取差异键名的 JavaScript 实战教程
技术百科
碧海醫心
发布时间:2026-01-27
浏览: 次 本文详解如何正确比较含数组、对象等嵌套结构的 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 oldValu
e = 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() 提升遍历效率。
通过以上改造,你将获得一个健壮、可预测的对象差异检测工具,精准定位需更新的字段,彻底规避引用误判陷阱。
# 的是
# 是一个
# 尤其是
# 为空
# 均为
# 两次
# 工具
# js
# json
# 递归
# 对象
# javascript
# java
# String
# 值类型
# typeof
# NULL
# map
# 比对
# 引用类型
# Object
# 遍历
# for
# 组中
# undefined
# date
# number
# Boolean
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- 如何在Golang中实现邮件发送功能_Golang
- PythonFastAPI项目实战教程_API接口
- Win11如何设置计划任务 Win11定时执行程序
- Win11怎么更改账户头像_Windows 11自
- Win11怎么设置默认终端应用_Windows11
- php能控制zigbee模块吗_php通过串口与c
- Win11怎么关闭键盘按键音_Win11禁用打字声
- Win11时间怎么同步到原子钟 Win11高精度时
- Windows Defender扫描失败怎么办_安
- c++如何判断文件是否存在_c++ filesys
- c++如何使用std::bind绑定函数参数_c+
- 如何在Golang中使用replace替换模块_指
- php中作用域操作符能访问私有静态属性吗_访问权限
- Mac如何整理桌面文件_Mac使用堆栈功能一键整理
- MAC怎么一键隐藏桌面所有图标_MAC极简模式切换
- Python音视频处理高级项目教程_FFmpegP
- 本地php环境出现502错误_nginx或apac
- Windows10如何更改任务栏高度_Win10解
- Mac如何开启夜览模式_Mac护眼模式设置与定时
- 如何高效识别并拦截拼接式恶意域名 spam
- c# 如何用c#实现一个支持优先级的任务队列
- Win11怎么查看显卡显存_查询Win11显卡详细
- php订单日志怎么在swoole写_php协程sw
- Win11如何设置电源计划_Win11电源计划优化
- 如何使用Golang管理跨项目依赖_Golang多
- 电脑的“网络和共享中心”去哪了_Windows 1
- Python字符串处理进阶_切片方法解析【指导】
- 如何使用Golang reflect检查方法数量_
- php8.4如何实现队列任务_php8.4redi
- Python异步编程高级项目教程_asyncio协
- Win11怎么看电池循环次数_Win11笔记本电池
- Win11如何设置开机问候语 Win11修改登录界
- 如何在Golang中实现微服务服务拆分_Golan
- win11 OneDrive怎么彻底关闭 Win1
- Python文本编码与解码_跨平台解析说明【指导】
- Win11怎么关闭系统声音_Win11系统提示音静
- c++23 std::expected怎么用 c+
- Python文件和流处理指南_高效读写大体积数据文
- Windows10系统怎么查看显卡型号_Win10
- c++怎么操作redis数据库_c++ hired
- Win11怎么开启远程桌面_Win11系统远程桌面
- Win10怎么更改用户名 Win10修改账户名称操
- 如何使用Golang处理静态文件缓存_提高页面加载
- PythonPandas数据分析项目教程_时间序列
- C++ STL算法库怎么用?C++常用算法函数(s
- Mac如何将HEIC图片格式转为JPG_Mac批量
- 如何在 Pandas 中按元素交集合并两列字符串
- Win10怎样安装Excel数据分析工具_Win1
- Linux怎么实现内网穿透_Linux安装Frp客
- mac怎么查看wifi密码_MAC查看已连接WiF


QQ客服