深拷贝与浅拷贝学习笔记

113 阅读2分钟

以下是浅拷贝与深拷贝的各种方法及其区别:

所需知识点:

typeof [],null,object 皆为object hasOwnProperty:过滤原型属性,只能获取自有属性

对象遍历:

    1. for in可以遍历可枚举属性,包括原型链上的属性;
    1. Object.keys返回一个由对象可枚举属性组成的数组,不包括原型属性;
    1. Object.getOwnProperty 返回对象的自有属性数组,包括可枚举的和不可枚举的,不包括原型属性

数组遍历:

    1. for in 输出数组的index,即'0','1','2','3','4','5'....
    1. Object.keys 同上
    1. Object.getOwnProperty 输出'0','1','2','3'...,'length'

测试数据:

    const target = {
     a: '1',
     e: [1, 2, 3, 4, [5, 6, [7, 8, [9]]]],
     b: {
       c: [{
         h: '1',
         i: '2'
       }],
       d: {
         f: {
           g: '4'
         }
       }
     },
     j: null,
     k: undefined,
     l: function () {
       console.log('l');
     },
     date: new Date(),
     reg: /[A-Z]/g,
   }

浅克隆

  const shallClone = (target) => {
    if (typeof target === 'object' && target !== null) {
      const cloneTarget = Array.isArray(target) ? [] : {}

      for (prop in target) {
        if (target.hasOwnProperty(prop)) {
          cloneTarget[prop] = target[prop]
        }
      }
      return cloneTarget
    } else {
      return target
    }
  }
  console.log('shallClone', shallClone(target));
  // target.b = {} //此层拷贝的是值
  // target.b.c = {} //此层拷贝的是引用
  // target.b.c.h = '7' //此层拷贝的是引用

运行结果: 再执行下面代码

target.b.c = {} //此层拷贝的是引用

运行结果: 经测试,shallClone方法除函数拷贝后是个空函数外基本上可以成功拷贝,但是对象值的对象值只拷贝了引用,而不是值,即对象只拷贝了一层。

深克隆

1、第一种方法

  function 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
    }
  }
  console.log('deepClone', deepClone(target));
  // target.b = {} //此层拷贝的是值
  // target.b.c = {} //此层拷贝的是值
  // target.b.c.h = '7' //此层拷贝的是值

运行结果: deepclone运行结果

经测试,deepClone方法可以拷贝整个对象的值,而不是引用,但是date格式和正则表达式拷贝失败,结果为空对象,函数拷贝失败,为空函数。

2、第二种方法

  const isObject = (target) => (typeof target === 'object') && target !== null;

  function deepClone2(target, map = new Map()) {
    Date.prototype.clone = function () {
      return new Date(this.valueOf());
    }
    RegExp.prototype.clone = function () {
      var pattern = this.valueOf();
      var flags = '';
      flags += pattern.global ? 'g' : '';
      flags += pattern.ignoreCase ? 'i' : '';
      flags += pattern.multiline ? 'm' : '';
      return new RegExp(pattern.source, flags);
    };
    // 先判断该引用类型是否被拷贝过
    if (map.get(target)) {
      return target;
    }
    // 检测当前对象target是否与 正则、日期格式对象匹配
    if (eval(target) instanceof RegExp || target instanceof Date) {
      console.log(target.clone())
      return target.clone()
    }
    if (isObject(target)) {
      map.set(target, true); // 为循环引用的对象做标记
      const cloneTarget = Array.isArray(target) ? [] : {};
      for (let prop in target) {
        if (target.hasOwnProperty(prop)) {
          cloneTarget[prop] = deepClone2(target[prop], map);
        }
      }
      return cloneTarget;
    } else {
      return target;
    }
  }
  console.log('deepClone2', deepClone2(target));
  // target.b = {} //此层拷贝的是值
  // target.b.c = {} //此层拷贝的是值
  // target.b.c.h = '7' //此层拷贝的是值

运行结果: 经测试,除函数外都能拷贝成功。

3、第三种方法

  function deepClone3(target) {
    return JSON.parse(JSON.stringify(target))
  }
  console.log('deepClone3', deepClone3(target));
  // target.b = {} //此层拷贝的是值
  // target.b.c = {} //此层拷贝的是值
  // target.b.c.h = '7' //此层拷贝的是值

运行结果: 经测试,除开undefined值和正则,函数不能拷贝,其他情况都能拷贝。