eq

const OBJECT_TYPE = '[object Object]'
const ARRAY_TYPE = '[object Array]'
const MAP_TYPE = '[object Map]'
const SET_TYPE = '[object Set]'
const REF_TYPES = [OBJECT_TYPE, ARRAY_TYPE, MAP_TYPE, SET_TYPE]

const getType = (target: unknown) => Object.prototype.toString.call(target)

export default function eq(target: any, other: any): boolean {
  const targetType = getType(target)
  const otherType = getType(other)
  if (targetType !== otherType) return false

  // 基础类型
  if (!REF_TYPES.includes(targetType)) return Object.is(target, other)

  if (targetType === OBJECT_TYPE) {
    const targetKeys = Object.keys(target)
    const otherKeys = Object.keys(other)
    if (targetKeys.length !== otherKeys.length) return false
    let n = targetKeys.length - 1

    while (n >= 0) {
      if (!eq(target[targetKeys[n]], other[targetKeys[n]])) return false
      n--
    }
    return true
  }

  if (targetType === ARRAY_TYPE) {
    if (target.length !== other.length) return false
    let n = target.length - 1
    while (n >= 0) {
      if (!eq(target[n], other[n])) return false
      n--
    }
    return true
  }

  if (targetType === MAP_TYPE) {
    return eq(Array.from(target), Array.from(other))
  }

  //
  return false
}