深浅拷贝

75 阅读2分钟

浅拷贝

创建一个对象 这个对象有着和原始对象属性值一样的拷贝结果

如果属性是基本类型 则拷贝的是基本类型的值

如果属性是引用类型(object Array等)拷贝的是内存地址 修改此对象会影响另一个对象

function simplyCopy(target) {  
    let newtarget = {}
    for(const key in target){
        /* 直接拷贝属性值 */
        newtarget[key] = target[key]
    }
    return newtarget
}

js 的 API

数组

  • splice concat都是浅拷贝

    特殊点:如果只有一层元素是深拷贝 后面层数是浅拷贝 拷贝引用地址

  • Object.assign(obj,sources) obj: 目标对象 sources:源对象

    特殊点:如果只有一级元素是深拷贝 后面级数是浅拷贝 拷贝引用地址

  • [...arr]

    特殊点:适用于数组 如果只有一级元素是深拷贝 后面级数是浅拷贝 拷贝引用地址

深拷贝

和浅拷贝基本相似 只不过拷贝出来的对象之间不会相互干扰

JSON.parse(JSON.stringify());/* 简单版 */
/* 函数不会被拷贝到 */ 

代码思路:

  • 对拷贝对象的属性值区分对待
  • 如果是基本数据类型 直接返回
  • 如果是引用类型 区分是数组还是对象
  • 边界处理 如果对象间接或直接引用自身引起对deepCopy()的循环
  • 其他引用类型 null Function set Map对其属性值的再一次拷贝
  • Bool Number String Date Error RegExp构造函数和原始数据
/* 
    使用WeakMap来处理循环克隆问题
    同时存在弱引用 
    当map(target,newtarget) 中
    如果是Map() 强引用类型 目标对象得到引用 即使是垃圾收回机制不能回收
*/
function deepCopy(target,map = new WeakMap()) {  
    /* 如果是基本类型 直接拷贝返回 */
    if(typeof target !== 'object')
        return target
    /* 区分数组和对象 还可通过构造器constructor*/
    let newtarget = Array.isArray(target) ? [] : {}
    if(map.has(target))
        return map.get(target)
    map.set(target,newtarget)
    for(let key in target){
        // console.log(key);
        /* 对引用类型的深拷贝中 会将Array转化成对象形式 */
        newtarget[key] = deepCopy(target[key])
    }
    return newtarget
}

完整版的策略模式

/**
 *
 * @param {Object} target
 * @param {WeakMap} map
 * @returns
 */
function demo(target, map = new WeakMap()) {
  /* 原始类型直接返回 */
  if (target == null || typeof target != 'object') {
    return target
  }
  /* 对循环引用的设置 */
  if (map.has(target)) {
    return map.get(target)
  }
  let newobj
  map.set(target, newobj)
  /* 策略模式 处理set map boolean date regexp array object function*/
  const typeObj = {
    Set: (target, map) => {
      let newobj = new Set()
      target.forEach(item => {
        newobj.add(demo(item, map))
      })
      return newobj
    },
    Map: (target, map) => {
      let newobj = new Map()
      target.forEach((item, index) => {
        newobj.set(index, demo(item, map))
      })
      return newobj
    },
    Boolean: target => {
      return new Boolean(target).valueOf
    },
    Date: target => {
      return new date(target).valueof()
    },
    RegExp: target => {
      return new RegExp(target).valueof()
    },
    Array: (target, map) => {
      let newobj = []
      target.forEach((item, index) => {
        newobj[index] = demo(target[index], map)
      })
      return newobj
    },
    Object: (target, map) => {
      let newobj = {}
      for (let i in target) {
        newobj[i] = demo(target[i], map)
      }
      return newobj
    },
    Function: target => {
      return new target()
    }
  }
  let type = Object.prototype.toString.call(target).slice(8, -1)
  if (typeObj && typeObj[type]) {
    return (newobj = typeObj[type](target, map))
  }
}