Fork me on GitHub

浅拷贝与深拷贝的实现方案与应用场景

写在前面

首先我们得清楚基本概念。拷贝(Copy)即复制。

浅拷贝:创建一个新对象,保存原始对象属性值精准拷贝。如果属性是基本类型,拷贝的是基本类型的值,如果属性是引用类型,拷贝的是内存地址,并不会占用新的内存,这种情况下如果其中一个对象改变了这个地址,会影响到另一个对象。浅拷贝只复制指向某个对象的指针,而不复制对象本身。新旧对象共享同一块内存

深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,增加了内存,且修改新对象不会影响原对象。新对象与原对象不共享内存

赋值和深/浅拷贝的区别(针对引用类型)

赋值:把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。

浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。

深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,前后的两个对象互不影响。

浅拷贝的实现方案

0x01 Object.assign()

把任意多个源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

let obj2 = Object.assign({}, obj1)

0x02 函数库lodash的_.clone方法

var _ = require('lodash');

var obj2 = _.clone(obj1);

0x03 展开运算符

同object.assign()功能相同

let obj2 = {...obj1}

0x04 Array.prototype.concat()

let arr2 = arr1.concat() // 返回新数组,但当数组中嵌套数组对象时为浅拷贝

0x05 Array.prototype.slice()

let arr2 = arr1.slice() // 返回新数组,但当数组中嵌套数组对象时为浅拷贝

深拷贝的实现方案

0x01 JSON.parse()和JSON.stringify()

let arr2 = JSON.parse(JSON.stringify(arr1));

缺点是不能处理函数和正则

0x02 函数库lodash的_.cloneDeep方法

var _ = require('lodash');

var obj2 = _.cloneDeep(obj1);

0x03 jQuery.extend()方法

$.extend(deepCopy,target,obj1,[objN]) // 第一个参数为true就是深拷贝

0x04 手写递归实现

解决循环引用的问题

function deepClone(obj, hash=new WeakMap()) {
  if(obj == null) return obj; // 不操作
  if(obj instanceof Date) return new Date(obj);
  if(obj instanceof RegExp) return new RegExp(obj);
  // 普通值/函数不需要深拷贝
  if(typeof obj !== "object") return obj;
  // 是对象的话要进行深拷贝
  if(hash.get(obj)) return hash.get(obj);
  let cloneObj = new obj.constructor();
  // 找到的是所属类原型上的constructor,而原型上的constructor指向的是当前类本身
  hash.set(obj. cloneObj);
  for(let key in obj) {
    if(obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], hash)
    }
  }
  return cloneObj;
}

深拷贝与浅拷贝的应用场景

无论是浅拷贝还是深拷贝,一般都用于操作Object 或 Array之类的复合类型。

比如想对某个数组 或 对象的值进行修改,但是又要保留原来数组 或 对象的值不被修改,此时就可以用深拷贝来创建一个新的数组 或 对象,从而达到操作(修改)新的数组 或 对象时,保留原来数组 或 对象。

场景:从服务器fetch到数据之后我将其存放在store中,通过props传递给界面,然后我需要对这堆数据进行修改,那涉及到的修改就一定有保存和取消,所以我们需要将这堆数据拷贝到其它地方。

在JS中有一些已经封装好的如数组方法:concat(),filter(),slice(),map()等,在修改数组时,不会修改原来的数组,而是返回一个新的数组。但这并不是真正的深拷贝,当数组中嵌套数组对象时仍为浅拷贝,嵌套数组的改变仍会影响原数组的值。

posted @ 2020-12-06 11:38  Lynn_z  阅读(2888)  评论(0编辑  收藏  举报