Skip to content

2628. 完全相等的 JSON 字符串

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 的类型系统:

  1. null 检查(typeof null === 'object',需要特殊处理)
  2. 类型不一致 → 直接 false
  3. 数组 → 逐元素递归,长度不同直接 false
  4. 普通对象 → 键数量一致后逐 key 递归
  5. 其他(基本类型已由 === 覆盖,走到这里说明不相等)

有两个地方做 Array.isArray 是冗余的——if (Array.isArray(o1) !== Array.isArray(o2)) 已经保证了类型一致,进入数组分支后不需要再检查 Array.isArray(o2)

与 lodash.isEqual 的差异

lodash.isEqual 处理了更多边界:NaN 的相等性、ArrayBufferMapSet、构造函数实例等。这里的 JSON 类型是受限的,只能包含 null、boolean、number、string、数组和纯对象——不需要处理那些复杂情况。