一、什么是浅克隆
浅克隆就是在克隆对象的时候,直接赋值,就可以算做浅克隆。
举个栗子:
<script>
/*
浅拷贝出现的前提:引用类型的数据(对象和数组)
*/
let obj1 = {
a : 1
}
let obj2 = obj1
obj2.a = 2
console.log(obj1.a); //输出:2
</script>
这里在修改obj2中a的值的时候,将源对象中a的值也修改了,所有我们可以看出,在克隆对象类型的数据时,直接赋值其实是得到的源对象的地址,在地址上修改的时候,会污染源对象的值。当然,基本数据类型不会出现这种情况。
所以我们有必要用另外一种方法来克隆引用类型的数据,于是深克隆孕育而生。。。
二、深克隆
当然,在深克隆的道路上,前辈们也做了很多努力。
1、JSON转换
用JSON将数据转换,这时候再赋值就能重新开辟一个空间,实现深克隆。
举个栗子:
<script>
let obj1 = {
a : 1,
b : undefined,
arr : [1,2,3],
fun : ()=>{
console.log('我是函数');
}
}
let obj2 = JSON.parse(JSON.stringify(obj1))
obj2.a = 2
obj2.arr[0] = 111
console.log(obj1,obj2);
</script>
</body>
打印结果:
看起来我们在修改obj2的值的时候,并没有污染源对象中a和arr[ ]数组的值,但是这里值得注意的是,obj2并没有拷贝obj1中b和fun函数。
这是因为JSON在转化的时候,数据类型为function或者数据值为undefined的情况下无法转化,导致了obj2并没有获得b和fun。
在数据类型不复杂的情况下,JSON转化是够用的,但是谁又没有感到无能为力的时候呢,于是我们接着来学习下面一种方法。
2、Object.assign({},obj)方法
首先来介绍一下Object.assign()方法:
Object.assign()
方法用于将一个或多个源对象的所有可枚举属性的值复制到目标对象。它返回目标对象。
语法:Object.assign(target,...sources)
target
:目标对象,即属性将被复制到的对象。
sources
:一个或多个源对象。
举几个栗子:
// 示例 1
const target = { a: 1, b: 2 };
const source1 = { b: 3, c: 4 };
const result1 = Object.assign(target, source1);
console.log(result1);
// 输出: { a: 1, b: 3, c: 4 }
// 示例 2
const target2 = {};
const source2 = { x: 5, y: 6 };
const result2 = Object.assign(target2, source2);
console.log(result2);
// 输出: { x: 5, y: 6 }
// 示例 3
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const obj3 = { c: 3 };
const merged = Object.assign({}, obj1, obj2, obj3);
console.log(merged);
// 输出: { a: 1, b: 2, c: 3 }
接下来放在我们的栗子里面:
let obj1 = {
a : 1,
b : undefined,
arr : [1,2,3],
fun : ()=>{
console.log('我是函数');
}
}
let obj2 = Object.assign({},obj1)
obj2.a = 2
obj2.arr[0] = 111
console.log(obj1,obj2);
输出结果:
我们可以看到:当在拷贝对象的时候,虽然解决了JSON不能拷贝undefined值和函数类型的问题,基本数据类型是可以进行深拷贝的,但是对于引用数据类型,依旧只能进行浅拷贝。
所以我们得出结论:Object.assign( )方法只能深拷贝一级属性,二级以上属性(引用类型)只能进行浅拷贝。
其实还有另外一种方法,扩展运算符(...)方法,用法和Object.assign( )方法一样,甚至出现的问题都是一样的。这里就不作过多的说明。
那我们有没有办法能解决以上的所有问题呢?答案是确定的:当然有!
3、递归
递归方法是比较麻烦的,但是是比较全面的。
我们自己写一个递归函数,进行赋值
栗子来了:
function ClonDeep(data){
let newData = Array.isArray(data) ? [] : {}
for (const key in data) {
if(data[key] && typeof data[key] ==='object'){
newData[key] = ClonDeep(data[key])
}else{
newData[key] = data[key]
}
}
return newData
}
let obj1 = {
a: 1,
b: undefined,
arr: [1, 2, 3],
fun: () => {
console.log('我是函数');
}
}
let obj2 = ClonDeep(obj1)
obj2.a = 2
obj2.arr[0] = 111
console.log(obj1, obj2);
运行结果:
可以看到,所有数据都进行了 克隆,而且没有出现相互影响的情况,所以是一个成功的深克隆方法。
当然缺点也很明显,就是太麻烦了!!!
所以在数据不复杂的情况下,还是建议用前面三种方法。