ChickenDreamFactory/fe-chicken

78.实现一个深拷贝

webVueBlog opened this issue · 0 comments

实现一个深拷贝

function myDeepCopy (obj, map = new WeakMap()) {

 if (typeof obj !== 'object') return;

 if (obj instanceof Date) {
  const copyDate = new Date();
  copyDate.setTime(obj.getTime());
  return copyDate;
 }

 if (obj instanceof RegExp) {
  const Constructor = obj.constructor;
  return new Constructor(obj);
 }

 let newObj = obj instanceof Array ? [] : {};
 if (map.get(obj)) {
  return map.get(obj);
 }

 map.set(obj, newObj);
 for(let key in obj) {
  if (obj.hasOwnProperty(key)) {
   newObj[key] = 
    obj[key] instanceof Object ? myDeepCopy(obj[key], map) : obj[key];
  }
 }

 return newObj;
}
const mapTag = '[object Map]'
const setTag = '[object Set]'
const arrayTag = '[object Array]'
const objectTag = '[object Object]'
const argsTag = '[object Arguments]'
const boolTag = '[object Boolean]'
const dateTag = '[object Date]'
const numberTag = '[object Number]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'
const errorTag = '[object Error]'
const regexTag = '[object RegExp]'
const funcTag = '[object Function]'

const deepTags = [
 mapTag,
 setTag,
 arrayTag,
 objectTag,
 boolTag,
 boolTag,
 dateTag,
 numberTag,
 stringTag,
 symbolTag,
 errorTag,
 regexTag,
 funcTag,
]

function forEach(array, iteratee) {
 let index = -1;
 const { length } = array
 while (++index < length) {
  iteratee(array[index], index)
 }

 return array
}

function isObject(target) {
 const type = typeof target
 return target && (type === 'object' || type === 'function')
}

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

function getInit(target) {
 const Ctor = target.constructor
 return new Ctor()
}

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

function cloneReg(target) {
 const reFlags = /\w*$/
 const result = new target.constructor(target.source, reFlags.exec(target);
 
 result.lastIndex = target.lastIndex
 return result
 )

 function cloneFunction(func) {
  const bodyReg = /(?<={)(.|\n)+(?=})/m
  const paramReg = /(?<=\().+(?=\)\s+{)/
  const funcString = func.toString()

  if (func.prototype) {
   const param = paramReg.exec(funcString)
   const body = bodyReg.exec(funcString)
   if (body) {
    if (param) {
     const paramArr = param[0].split(',')
     return new Function(...paramArr, body[0])
    }
    return new Function(body[0])
   }
   return null
  }
  return eval(funcString)
 }

 function cloneOtherType(target, type) {
  const Ctor = target.constructor
  switch (type) {
   case boolTag:
   case numberTag:
   case stringTag:
   case errorTag:
   case dateTag:
    return new Ctor(target)
   case regexTag:
    return new cloneReg(target)
   case symbolTag:
    return cloneFunction(target)
   case funcTag:
    return cloneFunction(target)
  
   default:
    return null
  }
 }

 function clone(target, map = new WeakMath()) {
  // 克隆原始类型
  if (!isObject(target)) return target

  // 初始化
  const type = getType(target)
  let cloneTarget

  if(deepTag.includes(type)) cloneTarget = getInit(target, type)
  else return cloneOtherType(target, type)

  // 防止循环引用
  if (map.get(target)) return target

  map.set(target, cloneTarget)

  // 克隆set
  if (type === setTag) {
   target.forEach((value) => cloneTarget.add(key, clone(value)))

   return cloneTarget
  }

  // 克隆对象和数组
  const keys = type === arrayTag ? undefined : Object.keys(target)
  forEach(keys || target, (value, key) => {
   if (keys) key = value
   cloneTarget[key] = clone(target[key], map)
  })

  return cloneTarget
 }
}
function deepCopy (obj, cache = []) {
 if (obj === null || typeof obj !== 'object') {
  return obj
 }
 if (Object.prototype.toString.call(obj) === '[object Date]') return new Date(obj)
 if (Object.prototype.toString.call(obj) === '[object RegExp]') return new RegExp(obj)
 if (Object.prototype.toString.call(obj) === '[object Error]') return new Error(obj)

 const item = cache.filter(item = > item.original === obj)[0]

 if (item) return item.copy

 let copy = Array.isArray(obj) ? [] : {}
 cache.push({
  original: obj,
  copy
 })

 Object.keys(obj).forEach(key => {
  copy[key] = deepCopy(obj[key], cache)
 })
 return copy
}
deepCopy($obj)