一、什么是深拷贝
在javascript中,简单数据类型的赋值是直接传递值,而复杂数据类型则是传递地址。而深拷贝就是指在拷贝时会开辟新的内存地址存放数据,而不是对原地址直接引用。
举个例子,如果我们只是将一个对象赋值给另一个变量,那么这两个变量指向的是同一个地址,修改其中一个对象会影响到另一个对象,这就是浅拷贝:
var obj1 = {a: 1, b: 2};
var obj2 = obj1;
obj2.a = 3;
console.log(obj1.a); // 3
而如果我们进行深拷贝,如下所示:
var obj1 = {a: 1, b: 2};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.a = 3;
console.log(obj1.a); // 1
这个时候,对象obj1的属性a并没有受到影响。
二、深拷贝的实现方式
1、递归实现深拷贝
深拷贝的实现方式有很多,其中一种比较常用的方式就是递归。
function clone(target){
let result;
if(typeof target === 'object'){
if(Array.isArray(target)){
result = [];
for(let i in target){
result.push(clone(target[i]));
}
}else if(target === null){
result = null;
}else if(target.constructor === RegExp){
result = target;
}else{
result = {};
for(let i in target){
result[i] = clone(target[i]);
}
}
}else{
result = target;
}
return result;
}
递归的方式就是遍历对象和数组,每一层都新建一个相同的对象或数组,如果遇到还是对象或数组就再次调用函数进行遍历。
但是该方法有一些缺点,会遇到一些环引用的情况(即对象中包含自己或循环引用),就会导致函数会陷入死循环,从而导致浏览器卡死。
2、使用JSON解析实现深拷贝
在JavaScript中,我们还可以通过JSON对象的解析方法进行深拷贝,如下所示:
var obj1 = {a: 1, b: {c: 2}};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b.c = 5;
console.log(obj1.b.c); // 2
通过JSON.parse(JSON.stringify())可以实现一个简单的深拷贝,但是该方法也有一些缺点:
- 该方法只适用于JSON格式可转的数据类型,对于一些不能转成JSON格式的数据类型如:function、Date等就不能实现深拷贝。
- 对于包含循环引用的对象或数据也无法正常解析,会进入死循环。
- 拷贝对象类型时,对象里的function、undefined等会被忽略。
3、使用第三方库进行深拷贝
除了手动实现深拷贝外,也可以使用一些第三方库来进行深拷贝,如lodash、jQuery等。
//使用lodash的cloneDeep()方法进行深拷贝
var obj1 = {a: 1, b: {c: 2}};
var obj2 = _.cloneDeep(obj1);
obj2.b.c = 5;
console.log(obj1.b.c); // 2
通过第三方库的方法进行深拷贝是最简单,最安全的方式。
三、深拷贝的使用方法
深拷贝在实际开发中也非常常用,在以下几个场景中使用:
- 在实现对象继承时,我们需要对父类对象进行深拷贝以保留其所有属性和方法。
- 在操作复杂的嵌套结构数据时,深拷贝能够保证不会影响到原数据。
- 当需要对某个对象或数组进行拷贝,而这个数据又需要改动时,可以使用深拷贝生成一个全新的数据,从而不影响原数据。
//深拷贝操作示例
var obj1 = {a: 1, b: {c: 2}};
var obj2 = _.cloneDeep(obj1);
obj2.b.c = 5;
console.log(obj1.b.c); // 2
console.log(obj2.b.c); // 5
四、深拷贝的优化
我们在使用深拷贝时需要注意,由于深拷贝的过程比较耗费内存资源,我们需要尽可能地减少深拷贝的次数。
比如,如果我们在Vue或React框架中需要对一个对象的某个值进行修改,我们可以直接替换改变对象的指向,而不是对整个对象进行深拷贝,从而提高程序的运行效率。
//Vue组件中示例
data(){
return {
obj: {
a: 1,
b: 2,
c: [3, 4, 5]
}
}
},
methods: {
changeValue(val){
this.obj.b = val;
}
}
在示例中,如果我们需要更改obj对象的b值,我们可以使用this.obj.b = val;的方式直接进行修改,不需要进行深拷贝。
五、总结
本文重点讲解了深拷贝的背景和实现方式,并给出了实际应用的场景和优化方法。在实际工作中,我们需要根据业务需求选择合适的深拷贝方式,避免出现不必要的问题。