深浅拷贝

108 阅读2分钟

浅拷贝

  • Object.assign();

Object.assign方法只会拷贝原对象自身的并且可以枚举的属性到目标对象

  1. 不会拷贝对象的继承属性。
  2. 不会拷贝对象的不可枚举属性。
  3. 可以拷贝Symbol类型的属性。
  • ...运算符

  • 原生数组拷贝的方式

  1. concat()
  2. slice()
  3. Array.from();
#浅拷贝
 copy(target) {
    if (typeof target === 'object' && target !== null) {
      const copy = Array.isArray(target) ? [] : {};
      for (const prop in target) {
        if (Object.hasOwnProperty.call(target, prop)) {
          copy[prop] = target[prop];
        }
      }

      return copy;
    }
    return target;
  }

深拷贝

  • JSON.stringify 最简单的深拷贝的方法,就是把一个对象序列化成为JSON的字符串,并将对象里面的内容转成字符串, 最后用JSON.parse()将JSON字符串生产一个新的对象。
    这种方式实现深拷贝有些地方需要注意。
  1. 拷贝的对象的值如果由函数,undefined, symbol这几种类型,经过JSON.stringify序列化后字符串中这个键值对会消失。
  2. 拷贝Date类型会变成字符串。
  3. 无法拷贝不可枚举的属性。
  4. 无法拷贝对象原型链。
  5. 拷贝正则引用类型会编程空对象。
  6. 对象中含有NaN, infinity 以及 -infinity, JSON序列化后的结果变成null
  7. 无法拷贝对象的循环引用,即对象成环 (obj[key]=obj)

真正的深拷贝

  • 解决上述的几个问题的思路
    • 如果是Date, RegExp 直接创建一个新的实例并返回
    • 如果是循环引用就用weakMap解决
    • 原型上的方法,可以结合 Object.getOwnPropertyDescriptors 获取对象上的所有属性及特性及 Object.getPrototypeOf原型上的方法和Object.create结合使用,创建一个新对象,并继承传入原对象的原型链。
    • 对象的不可枚举属性以及Symbol类型,可以使用Reflect.ownKeys方法
  copy(obj, hash = new WeakMap()) {
    // 日期类型返回一个新的日期类型
    if (obj instanceof Date) return new Date(obj);
    // 正则对象返回新的正则对象
    if (obj instanceof RegExp) return new RegExp(obj);
    // 循环引用使用 weakMap解决
    if (hash.has(obj)) return hash.get(obj);
    const desc = Object.getOwnPropertyDescriptors(obj);
    // 获取原型上的方法和对象的描述信息,创建新的对象
    const copyObj = Object.create(Object.getPrototypeOf(obj), desc);
    hash.set(obj, copyObj);
    // 循环遍历递归内容,防止还有内存共计的问题
    for (const key of Reflect.ownKeys(obj)) {
      const item = obj[key];
      if (typeof item === 'object' && item !== null && typeof item !== 'function') {
        copyObj[key] = this.copy(item);
      } else {
        copyObj[key] = item;
      }
    }
    return copyObj;
  }