Appearance
2628. 完全相等的 JSON 字符串
ts
type JSONValue =
| null
| boolean
| number
| string
| JSONValue[]
| { [key: string]: JSONValue }
function areDeeplyEqual(o1: JSONValue, o2: JSONValue): boolean {
if (o1 === o2) return true
if (o1 === null || o2 === null) return false
if (typeof o1 !== typeof o2) return false
if (Array.isArray(o1) !== Array.isArray(o2)) return false
if (Array.isArray(o1)) {
if (!Array.isArray(o2)) return false
if (o1.length !== o2.length) return false
for (let i = 0; i < o1.length; i++) {
if (!areDeeplyEqual(o1[i], o2[i])) return false
}
return true
}
if (typeof o1 === 'object' && typeof o2 === 'object') {
const keys1 = Object.keys(o1)
const keys2 = Object.keys(o2)
if (keys1.length !== keys2.length) return false
for (const key of keys1) {
if (!(key in o2)) return false
if (!areDeeplyEqual(o1[key], o2[key])) return false
}
return true
}
return false
}基本类型快速路径
第一行 if (o1 === o2) return true 看似简单,实际上覆盖了大量情况。对于基本类型(string, number, boolean, null),=== 已经完成了值比较。对于引用类型,只有指向同一对象时才为 true——这是提前终止递归的优化。
类型分发
后续的分支结构对应 JSON 的类型系统:
- null 检查(
typeof null === 'object',需要特殊处理) - 类型不一致 → 直接 false
- 数组 → 逐元素递归,长度不同直接 false
- 普通对象 → 键数量一致后逐 key 递归
- 其他(基本类型已由
===覆盖,走到这里说明不相等)
有两个地方做 Array.isArray 是冗余的——if (Array.isArray(o1) !== Array.isArray(o2)) 已经保证了类型一致,进入数组分支后不需要再检查 Array.isArray(o2)。
与 lodash.isEqual 的差异
lodash.isEqual 处理了更多边界:NaN 的相等性、ArrayBuffer、Map、Set、构造函数实例等。这里的 JSON 类型是受限的,只能包含 null、boolean、number、string、数组和纯对象——不需要处理那些复杂情况。
