looseEqual

栏目: source 发布时间:2025-10-15
/**
 * Check if two values are loosely equal - that is,
 * if they are plain objects, do they have the same shape?
 */
export function looseEqual(a: any, b: any): boolean {
  if (a === b) return true
  const isObjectA = isObject(a)
  const isObjectB = isObject(b)
  if (isObjectA && isObjectB) {
    try {
      const isArrayA = Array.isArray(a)
      const isArrayB = Array.isArray(b)
      if (isArrayA && isArrayB) {
        return (
          a.length === b.length &&
          a.every((e: any, i: any) => {
            return looseEqual(e, b[i])
          })
        )
      } else if (a instanceof Date && b instanceof Date) {
        return a.getTime() === b.getTime()
      } else if (!isArrayA && !isArrayB) {
        const keysA = Object.keys(a)
        const keysB = Object.keys(b)
        return (
          keysA.length === keysB.length &&
          keysA.every(key => {
            return looseEqual(a[key], b[key])
          })
        )
      } else {
        /* istanbul ignore next */
        return false
      }
    } catch (e: any) {
      /* istanbul ignore next */
      return false
    }
  } else if (!isObjectA && !isObjectB) {
    return String(a) === String(b)
  } else {
    return false
  }
}

这段代码实现了一个深度比较(deep comparison)的looseEqual函数,用于判断两个值是否“松散相等”。以下是逐层解读:

1. 核心逻辑框架

export function looseEqual(a: any, b: any): boolean {
  if (a === b) return true // 严格相等直接返回
  // ...后续处理
}

第一步:优先检查ab是否严格相等(包括基本类型值相等、对象引用相同)。若相等,直接返回true

2. 对象类型判断

const isObjectA = isObject(a)
const isObjectB = isObject(b)

通过isObject(未在代码中定义,通常实现为o => o != null && typeof o === 'object')判断是否为普通对象/数组/Date等复杂类型。

若两者都是对象,进入深度比较逻辑;若都不是对象,进入基本类型比较;否则返回false

3. 对象深度比较(核心)

3.1 数组比较

if (Array.isArray(a) && Array.isArray(b)) {
  return a.length === b.length && a.every((e, i) => looseEqual(e, b[i]))
}

条件:两者均为数组。

逻辑:

长度必须相同;

递归比较每个元素(looseEqual(e, b[i])),支持嵌套数组。

3.2 Date对象比较

if (a instanceof Date && b instanceof Date) {
  return a.getTime() === b.getTime()
}

条件:两者均为Date实例。 逻辑:比较时间戳(毫秒数)是否相等。

3.3 普通对象比较

const keysA = Object.keys(a)
const keysB = Object.keys(b)
return keysA.length === keysB.length && 
       keysA.every(key => looseEqual(a[key], b[key]))

条件:两者均为非数组普通对象。 逻辑: 对象的键数量必须相同; 递归比较每个键对应的值(支持嵌套对象)。

4. 非对象类型比较

return String(a) === String(b)

条件:a和b均为非对象类型(如字符串、数字等)。 逻辑:将值转为字符串后比较是否相等(例如1和'1'会被视为相等)。

5. 异常处理

try { /* ... */ } catch (e) { return false }

包裹整个对象比较逻辑,捕获可能的异常(如访问不可枚举属性、循环引用等),直接返回false

looseEqual({ a: { b: 1 } }, { a: { b: 1 } }) // true(嵌套对象)
looseEqual([1, [2]], [1, [2]])           // true(嵌套数组)
looseEqual(new Date(0), new Date(0))     // true(Date对象)
looseEqual(1, '1')                       // true(基本类型松散相等)
looseEqual(/a/, /a/)    

本文地址:https://www.tides.cn/p_vue-source-looseEqual