手写深拷贝

46 阅读2分钟

浅拷贝定义

创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝定义

将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

两种方法实现深拷贝:

  • JSON.parse(JSON.stringify(Obj))

    • 缺点 1 不支持 Date、正则、undefined、函数等数据
    • 缺点 2 不支持引用
  • 递归

先不考虑引用自身的情况,对复杂数据类型和简单数据类型分开处理,

function deepClone (a) {
  if (a instanceof Object) { // 不考虑 iframe 的情况
    // object
    let result = undefined
    if (a instanceof Function) { // 函数
      // 普通函数
      if (a.prototype) {
        result = function () { return a.apply(this, arguments) }
      } else {
        //  箭头函数
        result = () => { return a.apply(undefined, arguments) }
      }
    } else if (a instanceof Array) { // 数组
      result = []
    } else if (a instanceof Date) { // 日期
      result = new Date(a - 0)
    } else if (a instanceof RegExp) { // 正则
      result = new RegExp(a.source, a.flags)
    }
    for (let key in a) {
      result[key] = deepClone(a[key])
    }
  } else {
    // string number boolean undefined null bigint symbol
    return a
  }
}

如果有 a.self = a(循环引用),需要借助 Map 记录已经拷贝过的对象

此外,不拷贝原型上的属性

function deepClone (a, cache) {
  // 初始化 cache
  if (!cache) {
    cache = new Map()
  }
  if (a instanceof Object) {
    
    // 判断是否拷贝过当前对象
    if (cache.has(a)) { return cache.get(a) }
    
    // object
    let result
    if (a instanceof Function) { // 函数
      // 普通函数
      if (a.prototype) {
        result = function () { return a.apply(this, arguments) }
      } else {
        //  箭头函数
        // ...args表示所有传入的参数都会被打包成一个数组,赋值给 args 变量
        result = (...args) => { return a.call(undefined, ...args) }
      }
    } else if (a instanceof Array) { // 数组
      result = []
    } else if (a instanceof Date) { // 日期
      result = new Date(a - 0)
    } else if (a instanceof RegExp) { // 正则
      result = new RegExp(a.source, a.flags)
    } else {
      result = {}
    }
    
    cache.set(a, result)
    
    for (let key in a) {
      if (a.hasOwnProperty(key)) { // 不拷贝原型上的属性
        result[key] = deepClone(a[key], cache)
      }
    }
    return result
  } else {
    // string number boolean undefined null bigint symbol
    return a
  }
}


// 测试
const a = {
  number: 1, bool: false, str: 'hi', empty1: undefined, empty2: null,
  array: [
    { name: 'frank', age: 18 },
    { name: 'jacky', age: 19 }
  ],
  date: new Date(2000, 0, 1, 20, 30, 0),
  regex: /.(j|t)sx/i,
  obj: { name: 'frank', age: 18 },
  f1: (a, b) => a + b,
  f2: function (a, b) { return a + b }
}
a.self = a

const b = deepClone(a)