首先,深拷贝和浅拷贝针对的是对象类型(对象,数组,函数)
浅拷贝指的是只是拷贝了对象的引用地址,彼此之间高耦合,一个改变,另一个可能也随之改变;
深拷贝是指只是完整的将变量的值拷贝过来,是一个新的对象,和被拷贝对象解耦合,一个改变,不会影响其它的内容。
浅拷贝
1.最浅的拷贝--对象直接赋值,整个地址拷贝
let a = { age: 1 } let b = a; a.age = 2 console.log(b.age) // 2
2.一级浅拷贝(无嵌套)--->每个属性的值都是原始类型的值
1)对象遍历赋值
let copy = function(target,source){ for(let property in source){ target[property] = source[property] } return target; }
2) 对象合并函数Object.assign({})--复制可遍历属性(元属性enumerable为true)
当Object.assign(obj)只有一个参数时,相当于最浅拷贝, let copy = Object.assign(obj); 等同于: let copy = obj; // 注意:当obj的值为原始类型的值(boolean,string,number)时,会将原始值转为包装对象。当obj为undefined或者null时,无法转为对象会报错。 当Object.assign(target, source1, source2,...)有多个参数时,可以将第一个设为空对象{} let copy = Object.assign({}, source); 等同于:(1)中的对象遍历属性赋值
// 注意: 当source为undefined或者null或者(boolean,number)时,会忽略source;当source为string类型时,会将字符串转为对象 Object.assign({},str) ---> //{0: "a", 1: "b", 2: "c"}
3)扩展运算符(...)和(1)类似
let a = { age: 1 } let b = {...a} a.age = 2 console.log(b.age) // 1
注意: 以上三种方法仅限于对象的属性值非对象类型,如果是对象类型,则属性值是对象的属性仅引用地址拷贝
eg: let a = {b: {b1: {b2: 3}}, c: {c1: 2}, d: 5};
1)let target1 = copy({}, a);
2)let target2 = Object.assign({},a);
3)let target3 = {...a};
当修改a.b = 100; target1,target2,target3都同时修改;
当修改a.d = 99; target1,target2,target3都保持不变;
深拷贝
为了不出现上面例子中,嵌套关系拷贝只拷贝地址的情况,我们需要遍历到最底层,逐级赋值,实现深拷贝
1.JSON.parse(JSON.stringify())--->可遍历属性
该方法可以解决大多数的深拷贝问题。
但是有局限性:
1)JSON.stringify()忽略属性值为undefined和function类型和Symbol类型的值;
2) JSON.stringify()序列化值为Regex,Error类型的值会变成空对象{};
3) JSON.stringify()序列化NaN,Infinity,-Infinity的值会变成null;
3)不能序列化循环引用的对象,会抛出异常
4)当参数对象有自定义的toJSON()方法时,JSON.stringify()会将该方法的返回值作为最后的参数值,忽然其它的参数。
5)当参数值为Date对象类型时,因为Date对象有toJSON()方法,则JSON.stringify()会将Date类型的值按照字符串转换,
然后JSON.parse()去解析JSON字符串,最终会解析成字符串,不会解析成Date类型。
2.通用的深拷贝方法(解决方法1中的局限问题)
1)直接使用lodash中的cloneDeep方法
2)手写一个深拷贝方法