JSON本身不提供深度比对能力,仅是字符串格式;真正需比对的是解析后的JS对象,核心是用Object.is()递归遍历类型、键名与值,识别增删改,并妥善处理NaN、-0/+0、循环引用及Date/RegExp等特殊类型。

JSON 本身不是一种可比的数据结构,它只是字符串格式;真正需要对比的是解析后的 JavaScript 对象(或数组)。深度对比差异,核心是逐层检查类型、键名、值是否一致,并识别新增、删除、修改项——不能靠 === 或 JSON.stringify(),前者只比引用,后者会丢数据、错判顺序、无法处理 undefined 和 NaN。
用 Object.is() + 递归遍历是最轻量可靠的方案
适合大多数业务场景(如表单变更检测、状态 diff),不依赖外部库,逻辑清晰可控:
- 用
Object.is(a, b)替代===,能正确处理NaN === NaN、-0和+0等边界情况 - 先判断是否同为对象且非 null,再区分数组与普通对象:数组按索引比,对象按 key 遍历
- 对每个 key,若新旧值不等,进一步判断:都是纯对象就递归;否则直接记录变更
- 单独扫描旧对象的 key,检查哪些在新对象中已消失 → 标记为
"deleted"
处理特殊类型要提前拦截
原生 JSON 不支持函数、Date、RegExp、Map、Set、Symbol、undefined,但实际 JS 对象中可能出现。对比前需统一策略:
-
Date→ 比.getTime() -
RegExp→ 比.toString() -
Map/Set→ 转成数组再比(如[...map]) -
undefined和Symbol→JSON.stringify会忽略,递归中需显式保留并比较 - 循环引用 → 手写递归需加 visited 缓存,否则栈溢出;建议优先规避或改用
lodash.isEqual
生产环境推荐成熟库而非手写
当需求变复杂(如生成 JSON Patch、逆向应用差异、忽略某些路径、支持自定义比较器),手写容易遗漏边界。这几个库经过大量验证:
立即学习“Java免费学习笔记(深入)”;
-
deep-diff:返回结构化变更列表(
N新增、D删除、E修改、A数组变动),支持浏览器和 Node.js - jsondiffpatch:专注差异生成与回放,delta 可序列化、可压缩,适合前后端同步场景
- lodash.isEqual:最稳的通用 deepEqual,自动处理循环引用、多种内置类型,但不直接输出差异详情
避免踩坑的两个关键点
很多“看似相等”的对象对比失败,往往卡在这两处:
-
属性顺序无关性:
{"a":1,"b":2}和{"b":2,"a":1}语义相同,但JSON.stringify输出不同 → 必须结构比,不能字符串比 -
数组 vs 对象误判:
Array.isArray()必须显式判断,否则[]和{}在递归中会被当作同类处理,导致漏比或报错


















