Appearance
2700. 两个对象之间的差异
ts
type JSONValue =
| null
| boolean
| number
| string
| JSONValue[]
| { [key: string]: JSONValue }
type Obj = Record<string, JSONValue> | Array<JSONValue>
const isObject = (object: unknown) =>
typeof object === 'object' && object !== null
function type(obj: unknown): string {
return Object.prototype.toString.call(obj).slice(8, -1)
}
function objDiff(obj1: Obj, obj2: Obj): Obj {
// 类型不同
if (type(obj1) !== type(obj2)) return [obj1, obj2]
// 基础类型比较
if (!isObject(obj1)) return obj1 === obj2 ? {} : [obj1, obj2]
// 过滤非共同子项
const sameKeys = Object.keys(obj1).filter((key) => Object.hasOwn(obj2, key))
const diff: Obj = {}
// 遍历相同key
for (const key of sameKeys) {
const sub = objDiff((obj1 as any)[key], (obj2 as any)[key])
if (Object.keys(sub).length > 0) {
(diff as any)[key] = sub
}
}
return diff
}diff 的是什么
和 areDeeplyEqual 不同,objDiff 不是返回布尔值,而是返回一个差异对象,只保留不同的部分。
设计上的几个决策:
1. 类型不同 → 返回 [obj1, obj2]。 原始值和最终值一起返回,调用方可以知道「从什么变成了什么」。
2. 基本类型相等 → 返回空对象 `{}``。 差异结果只记录不同的部分,相等的属性会被过滤掉。递归向上时,如果子对象的 diff 是空对象(即所有子属性都相等),这个 key 也不会出现在最终结果中。
3. 只比较共同 key。 如果 obj1 有而 obj2 没有(或反之)的 key,不纳入 diff 结果。这意味着这里返回的是“共同结构里的差异”,而不是完整增删改对比。
性能考虑
Object.keys(obj1).filter + Object.hasOwn(obj2, key) 对每个对象都调用一次,嵌套层级深时会有大量 key 枚举。如果对象键数量很大,考虑用 Set 优化。
另外,Object.prototype.toString.call(obj) 比 typeof 更精确(能区分数组和对象),但每次调用都有函数调用和字符串切片开销。在递归中使用时可以缓存类型检查结果。
