js实现递归深拷贝

97 阅读1分钟
// 注意点:
// 1. 避免循环引用
// 2. 对更多的引用类型值进行不同的克隆操作

const MAP_TAG = '[object Map]'
const SET_TAG = '[object Set]'
const ARRAY_TAG = '[object Array]'
const OBJECT_TAG = '[object Object]'
const BOOLEAN_TAG = '[object Boolean]'
const DATE_TAG = '[object Date]'
const ERROR_TAG = '[object Error]'
const NUMBER_TAG = '[object Number]'
const REGEXP_TAG = '[object RegExp]'
const STRING_TAG = '[object String]'
const SYMBOL_TAG = '[object Symbol]'
const FUNCTION_TAG = '[object Function]'
const CAN_TARVERSE_TAGS = [MAP_TAG, SET_TAG, ARRAY_TAG, OBJECT_TAG]
const CANT_CLONE_TAGS = [ERROR_TAG, FUNCTION_TAG]

function isReference(target) {
  return (
    target !== null &&
    (typeof target === 'object' || typeof target === 'function')
  )
}

function getType(target) {
  return Object.prototype.toString.call(target)
}

function getInitialReference(target) {
  return new target.constructor()
}

function cloneCantTarverseReference(target, type) {
  function cloneRegExp(regexp) {
    const reFlags = /\w*$/
    const result = new regexp.constructor(regexp.source, reFlags.exec(regexp))
    result.lastIndex = regexp.lastIndex
    return result
  }

  function cloneSymbol(targe) {
    return Object(Symbol.prototype.valueOf.call(targe))
  }

  if (CANT_CLONE_TAGS.includes(type)) {
    return target
  }

  if ([BOOLEAN_TAG, NUMBER_TAG, STRING_TAG, DATE_TAG].includes(type)) {
    return new target.constructor(target)
  } else if (type === REGEXP_TAG) {
    return cloneRegExp(target)
  } else if (type === SYMBOL_TAG) {
    return cloneSymbol(target)
  } else {
    return null
  }
}

function cloneCanTarverseReference(originTarget, cloneTarget, type, cycleMap) {
  function cloneMap() {
    for (const [key, value] of originTarget) {
      cloneTarget.set(key, cloneDeep(value, cycleMap))
    }
  }

  function cloneSet() {
    for (const value of originTarget) {
      cloneTarget.add(cloneDeep(value, cycleMap))
    }
  }

  function cloneArray() {
    for (const value of originTarget) {
      cloneTarget.push(cloneDeep(value, cycleMap))
    }
  }

  function cloneObject() {
    for (const [key, value] of Object.entries(originTarget)) {
      cloneTarget[key] = cloneDeep(value, cycleMap)
    }
  }

  if (type === MAP_TAG) {
    cloneMap()
  } else if (type === SET_TAG) {
    cloneSet()
  } else if (type === ARRAY_TAG) {
    cloneArray()
  } else if (type === OBJECT_TAG) {
    cloneObject()
  }
}

function cloneDeep(target, cycleMap = new Map()) {
  // 非引用类型直接返回
  if (!isReference(target)) return target

  const referenceType = getType(target)

  // 不可遍历的引用类型值处理
  if (!CAN_TARVERSE_TAGS.includes(referenceType)) {
    return cloneCantTarverseReference(target, referenceType)
  }

  // 可遍历的引用类型值,防止循环引用
  const pureReference = getInitialReference(target)
  if (cycleMap.has(target)) {
    return cycleMap.get(target)
  } else {
    cycleMap.set(target, pureReference)
  }

  cloneCanTarverseReference(target, pureReference, referenceType, cycleMap)
  return pureReference
}