深拷贝

概念

深拷贝是将一个对象从内存中完整的拷贝一份,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。新对象跟原对象不共享内存

举例

const obj1 = {
    name: "sun",
    age: 18,
    favour: ["ball", "games", "study"],
    birthCity: {
        Province: "四川"
    }
}

// 深拷贝
const obj2 = deepClone(obj1)
function deepClone(params) {
    // 深拷贝方法,后续会实现
}

obj2.favour[0] = "a"
console.info(obj1)  //  => obj1.favour = ["ball", "games", "study"]

实现

  1. 对于最简单的实现
JSON.parse(JSON.stringify(obj))
// 存在很大的弊端,只能拷贝纯对象,数组,不能拷贝正则,日期对象等,无法处理循环引用问题
// 循环引用,即obj.a = obj
  1. 简易版
// 使用递归深拷贝,但无法解决循环引用问题
const deepClone = target => {
    if (typeof target === 'object' && target !== null) {
        const cloneTarget = Array.isArray(target) ? [] : {}
        for (let prop in target) {
            if (target.hasOwnProperty(prop)) {
                cloneTarget[prop] = deepClone(target[prop])
            }
        }
        return cloneTarget
    } else {
        return target
    }
}
  1. 解决循环引用版深拷贝
const isObject = target => (typeof target === "object" || typeof target === "function") && target !== null

const deepClone = (target, map = new weakMap()) => {
    if (map.get(target)) return target

    if (isObject(target)) {
        map.set(target, true)
        const cloneTarget = Array.isArray(target) ? [] : {}
        for (let prop in target) {
            if (target.hasOwnProperty(prop)) {
                cloneTarget[prop] = deepClone(target[prop], map)
            }
        }
        return cloneTarget
    } else {
        return target
    }
}
const a = {value: 1}
a.target = a
let newA = deepClone(a)
console.info(newA)//{ val: 2, target: { val: 2, target: [Circular] } }
  1. 完整的深拷贝
// 考虑到了多种情况

const getType = obj => Object.prototype.toString.call(obj)

const isObject = target => (typeof target === "object" || typeof target === "function") && target !== null

// 可以遍历的对象
const canTraverse = {
    "[object Map]": true,
    "[object Set]": true,
    "[object Array]": true,
    "[object Object]": true,
    "[object Arguments]": true
}

const mapTag = '[object Map]'
const setTag = '[object Set]'
const boolTag = '[object Boolean]'
const numberTag = '[object Number]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'
const dateTag = '[object Date]'
const errorTag = '[object Error]'
const regexpTag = '[object RegExp]'
const funcTag = '[object Function]'

const handleRegExp = target => {
    const { source, flags } = target
    return new target.constructor(source, flags)
}

// 处理函数
const handleFunc = func => {
    // 箭头函数直接返回自身,根据原型判断是否为箭头函数
    if (!func.prototype) return func
    const bodyReg = /(?<={)(.|\n)+(?=)})/m
    const paramReg = /(?<=\().+(?=\)\s+{)/
    const funcString = func.toString()
    const param = paramReg.exec(funcString)
    const body = bodyReg.exec(funcString)
    if (!body) return null
    if (param) {
        const paramArr = param[0].split(',')
        return new Function(...paramArr, body[0])
    } else {
        return new Function(body[0])
    }
}

const handleNotTraverse = (target, tag) => {
  const Ctor = target.constructor;
  switch(tag) {
    case boolTag:
      return new Object(Boolean.prototype.valueOf.call(target));
    case numberTag:
      return new Object(Number.prototype.valueOf.call(target));
    case stringTag:
      return new Object(String.prototype.valueOf.call(target));
    case symbolTag:
      return new Object(Symbol.prototype.valueOf.call(target));
    case errorTag: 
    case dateTag:
      return new Ctor(target);
    case regexpTag:
      return handleRegExp(target);
    case funcTag:
      return handleFunc(target);
    default:
      return new Ctor(target);
  }
}

const deepClone = (target, map = new WeakMap()) => {
  if(!isObject(target)) 
    return target;
  let type = getType(target);
  let cloneTarget;
  if(!canTraverse[type]) {
    // 处理不能遍历的对象
    return handleNotTraverse(target, type);
  }else {
    // 这波操作相当关键,可以保证对象的原型不丢失!
    let ctor = target.constructor;
    cloneTarget = new ctor();
  }

  if(map.get(target)) 
    return target;
  map.set(target, true);

  if(type === mapTag) {
    //处理Map
    target.forEach((item, key) => {
      cloneTarget.set(deepClone(key, map), deepClone(item, map));
    })
  }

  if(type === setTag) {
    //处理Set
    target.forEach(item => {
      cloneTarget.add(deepClone(item, map));
    })
  }

  // 处理数组和对象
  for (let prop in target) {
    if (target.hasOwnProperty(prop)) {
        cloneTarget[prop] = deepClone(target[prop], map);
    }
  }
  return cloneTarget;
}

results matching ""

    No results matching ""