json本身不提供深度比对能力,仅是数据交换格式;需通过递归遍历或成熟库(如deep-diff、jsondiffpatch)实现对象深度差异识别,核心是逐层比较类型、属性与值,并妥善处理特殊类型及边界情况。

JavaScript 中 JSON 本身不提供深度比对能力,它只是数据交换格式;要实现对象的深度差异比对,需借助 JavaScript 代码手动递归遍历,或使用成熟库辅助。核心在于:把两个对象(或 JSON 字符串解析后的对象)逐层比较属性、类型、值,识别新增、删除、修改项。
基础思路:递归遍历 + 类型判断
深度比对不能只用 === 或 JSON.stringify(),因为后者对属性顺序敏感、忽略 undefined、无法区分 0 和 false 等。正确做法是:
- 先判断是否为同类型(都为 object、且非 null、非数组或同为数组)
- 若为普通对象,遍历所有 ownKeys(包括 symbol),对每个 key 递归比较值
- 若为数组,按索引比对(注意长度不同时的增删识别)
- 遇到函数、Date、RegExp、Map、Set 等特殊类型需单独处理(JSON 本就不支持,但比对逻辑中需考虑)
简易手写 diff 函数(适用于纯 JSON 兼容对象)
以下是一个轻量、可读性强的深度差异提取示例,返回一个描述变更的结构:
function diffObj(oldObj, newObj) {
const result = {};
// 找出 oldObj 中有、newObj 中没有的 key(删除)
Object.keys(oldObj).forEach(key => {
if (!(key in newObj)) {
result[key] = { type: 'deleted', oldValue: oldObj[key] };
}
});
// 找出 newObj 中有、oldObj 中没有的 key(新增)
Object.keys(newObj).forEach(key => {
if (!(key in oldObj)) {
result[key] = { type: 'added', newValue: newObj[key] };
} else {
const oldVal = oldObj[key];
const newVal = newObj[key];
// 深度比较:同为对象且非 null、非 Date/RegExp 等
if (isPlainObject(oldVal) && isPlainObject(newVal)) {
const nestedDiff = diffObj(oldVal, newVal);
if (Object.keys(nestedDiff).length > 0) {
result[key] = { type: 'changed', diff: nestedDiff };
}
} else if (oldVal !== newVal) {
result[key] = { type: 'changed', oldValue: oldVal, newValue: newVal };
}
}
});
return Object.keys(result).length ? result : null;
}
function isPlainObject(obj) {
return obj !== null && typeof obj === 'object' && !Array.isArray(obj) && !(obj instanceof Date) && !(obj instanceof RegExp);
}
推荐实用方案:用现成库避免重复造轮子
生产环境建议使用经过充分测试的库,它们已处理边界情况(如循环引用、BigInt、Proxy、不可枚举属性等):
- deep-diff:轻量,返回标准 diff 数组,含 kind(E/N/A/D)、path、lhs/rhs 等字段
- jsondiffpatch:功能全面,支持生成 patch、反向 patch、格式化输出,适合前后端协同场景
- lodash.isEqual + 自定义逻辑:用于判断是否相等;如需差异详情,配合 lodash.differenceWith 或扩展
注意事项与常见坑
实际使用中容易忽略的关键点:
- JSON.parse() 后的对象丢失方法、undefined、Symbol、原型链 —— 若原始对象含这些,比对前需明确是否只关注“可序列化部分”
- 浮点数精度、+0/-0、NaN 的相等判断需用
Object.is()而非=== - 日期对象应转为时间戳再比对,否则
new Date(2020,0,1) !== new Date(2020,0,1) - 如果要支持循环引用,必须维护已访问对象的 WeakMap 缓存,否则栈溢出
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











