概念

在 Javascript 中,浅拷贝和深拷贝是只针对 Object、Array这样的引用类型的对象的。浅拷贝是只复制了对象的第一层属性,当复制到的属性是对象时,其实只是复制了内存中的地址,当被复制对象的这个属性的属性发生变更时,复制对象也会跟着发生变化。而深拷贝是递归复制了所有层级,与原对象彻底脱离了关系。

实现浅拷贝

使用扩展运算符

  1. let person = {'age':18, 'name': 'ruijie'};
  2. let personCopy = {...person};
  3. console.log(personCopy);

使用Object.assign()

  1. let person = {'age':18, 'name': 'ruijie'};
  2. let personCopy = {};
  3. Object.assign(personCopy, person);
  4. console.log(personCopy);

实现深拷贝

使用序列化和反序列化

先使用 JSON.stringify() 将对象序列化成字符串,再用 JSON.parse() 反序列化。

  1. let person = { name: 'ruijie', age: 18, child: { age: 12 }};
  2. let personCopy = JSON.parse(JSON.stringify(person));
  3. personCopy.child.age = 88;
  4. console.log(person.child.age)

但是这个方法有缺陷,当属性遇到 JS 的内置类型时,会丢失这个属性值,遇到Date对象会自动序列化,如果是方法则丢失这个属性值,如下:

  1. console.log(JSON.parse(JSON.stringify({ a: new Map([[1,2],['a','b']]) })));
  2. //{ a: {} }
  3. console.log(JSON.parse(JSON.stringify({ a: new Set([1,2,3,4]) })));
  4. //{ a: {} }
  5. console.log(JSON.parse(JSON.stringify({ a: new RegExp() })));
  6. //{ a: {} }
  7. console.log(JSON.parse(JSON.stringify({ a: new ArrayBuffer([12,4]) })));
  8. //{ a: {} }
  9. console.log(JSON.parse(JSON.stringify({ a: new Date() })));
  10. //{ a: '2021-10-21T10:30:37.338Z' }
  11. console.log(JSON.parse(JSON.stringify({ a: function () { } })));
  12. //{}

递归拷贝

实现深拷贝有几个要点

  • 利用 undefined == null 来同时过滤 null 和 undefined 类型,直接返回。
  • 利用 typeof 判断类型,如果不是对象就直接返回。
  • 这边还要特殊处理下内置对象,Date、RegExp等。
  • 如果是对象获取对象的构造函数,因为传入的值可能是对象或者数组,通过构造函数可以直接创建一个新对象或数组。
  • 利用 WeakMap 保存旧对象和新对象的键值对,当检索到已经存在时,直接返回,解决循环引用问题。 ```javascript // 自己实现深拷贝 (递归拷贝 要一层层的拷贝) // 掌握类型判断 typeof instanceof Object.prototype.toString.call constructor // null == undefined => true
    // null === undefined => false // for in 会循环原型对象上的属性 // 利用 map weakMap set 集合 map 映射表 解决循环引用 如:obj.xxx = obj; function deepClone(obj, hash = new WeakMap()) { //判断obj是null还是undefined if (obj == null) return obj; if (typeof obj !== ‘object’) return obj; //不是对象就不用拷贝了 if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj);

    if (hash.has(obj)) return hash.get(obj); //如果weakMap中有对象就直接返回

    // 要不是数组 要不是对象 let cloneObj = new obj.constructor;

    //如果是对象把它放到weakMap中,如果拷贝这个对象这个对象就存在 直接返回这个对象即可。 hash.set(obj, cloneObj); for (let key in obj) { //实现深拷贝

    1. if (obj.hasOwnProperty(key)) {
    2. cloneObj[key] = deepClone(obj[key], hash);
    3. }

    } return cloneObj; }

// map weakMap set 集合 map 映射表 let obj = { age: { name: 12 } } //循环引用 obj.xxx = obj; //抛出异常 let n = deepClone([1, 2, 3]); let m = deepClone(obj); obj.age.name = 88; console.log(n); console.log(m); ```