深拷贝与浅拷贝的实现⽅案与应⽤场景
1 写在前⾯
⾸先我们得清楚基本概念。拷贝(Copy)即复制。
浅拷贝:创建⼀个新对象,保存原始对象属性值精准拷贝。如果属性是基本类型,拷贝的是基本类型的值,如果属性是引⽤类型,拷贝的是内存地址,并不会占⽤新的内存,这种情况下如果其中⼀个对象改变了这个地址,会影响到另⼀个对象。浅拷贝只复制指向某个对象的指针,⽽不复制对象本⾝。新旧对象共享同⼀块内存。
深拷贝:将⼀个对象从内存中完整的拷贝⼀份出来,从堆内存中开辟⼀个新的区域存放新对象,增加了内存,且修改新对象不会影响原对象。新对象与原对象不共享内存。
2 赋值和深/浅拷贝的区别(针对引⽤类型)
赋值:把⼀个对象赋值给⼀个新的变量时,赋的其实是该对象的在栈中的地址,⽽不是堆中的数据。
浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引⽤类型因共享同⼀块内存,会相互影响。
深拷贝:从堆内存中开辟⼀个新的区域存放新对象,对对象中的⼦对象进⾏递归拷贝,前后的两个对象互不影响。
3 浅拷贝的实现⽅案
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 at()
let arr2 = at() // 返回新数组,但当数组中嵌套数组对象时为浅拷贝
0x05 Array.prototype.slice()
let arr2 = arr1.slice() // 返回新数组,但当数组中嵌套数组对象时为浅拷贝
4 深拷贝的实现⽅案
0x01 JSON.parse()和JSON.stringify()
let arr2 = JSON.parse(JSON.stringify(arr1));
缺点是不能处理函数和正则
0x02 函数库lodash的_.cloneDeep⽅法
var _ = require('lodash');
var obj2 = _.cloneDeep(obj1);
d()⽅法
$.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;
// 是对象的话要进⾏深拷贝
(obj)) (obj);
let cloneObj = structor();
typeof array
// 到的是所属类原型上的constructor,⽽原型上的constructor指向的是当前类本⾝
hash.set(obj. cloneObj);
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash)
}
}
return cloneObj;
}
4 深拷贝与浅拷贝的应⽤场景
⽆论是浅拷贝还是深拷贝,⼀般都⽤于操作Object 或 Array之类的复合类型。
⽐如想对某个数组 或 对象的值进⾏修改,但是⼜要保留原来数组 或 对象的值不被修改,此时就可以⽤深拷贝来创建⼀个新的数组 或 对象,从⽽达到操作(修改)新的数组 或 对象时,保留原来数组 或 对象。
场景:从服务器fetch到数据之后我将其存放在store中,通过props传递给界⾯,然后我需要对这堆数据进⾏修改,那涉及到的修改就⼀定有保存和取消,所以我们需要将这堆数据拷贝到其它地⽅。
在JS中有⼀些已经封装好的如数组⽅法:concat(),filter(),slice(),map()等,在修改数组时,不会修改原来的数组,⽽是返回⼀个新的数组。但这并不是真正的深拷贝,当数组中嵌套数组对象时仍为浅拷贝,嵌套数组的改变仍会影响原数组的值。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。