一、实现深拷贝之前,我们要先搞明白深拷贝的定义是什么?
在JavaScript中,数据类型分为基本数据类型和引用数据类型两种,对于基本数据类型来说,它的值直接存储在栈内存中,而对于引用类型来说,它在栈内存中仅仅存储了一个引用,而真正的数据存储在堆内存中
因此,在进行赋值操作时,会因为不同的类型产生不同的效果,简单举例一下。
基本类型:
let a = 1, b = a;
b = 2
console.log(a);//1
console.log(b);//2
引用类型:
let obj = {
a:1,
b:2
},
obj2 = obj;
obj2.a = 11;
console.log(obj.a); //11
console.log(obj1.a); //11
显然,两个对象的值都被修改了。
当值是引用类型时,修改obj2内部的属性值会导致obj的值也被修改。我们其实仅仅只是将 obj1 存储在栈堆中的的引用赋予了 obj2 ,而两个对象此时指向的是在堆内存中的同一个数据,所以当我们修改任意一个值的时候,修改的都是堆内存中的数据,而不是引用,所以只要修改了,同样引用的对象的值也自然而然的发生了改变
二、进入主题,深拷贝的实现
1、浅拷贝
先来看看浅拷贝的实现
let obj = {
a: 1,
b: 2
}
let obj2 = Object.assign({}, obj1);
Object.assign的拷贝,是对于第一层属性的拷贝,所以是浅拷贝。
let obj = {
a: 1,
b: 2,
c: {d:3}
}
let obj2 = Object.assign({}, obj1);
obj2.c.d = 5
console.log(obj1.c); // 5
console.log(obj2.c); // 5
2、深拷贝
第一种:乞丐版深拷贝
let obj = {
a: 1,
b: 2,
c: 3
}
let obj2 = JSON.parse(JSON.stringify(obj));
obj2.a = 5;
console.log(obj.a); // 1
console.log(obj2.a); // 5
第二种:
let obj = {
a: 1,
b: 2
}
let obj2 = Object.assign({}, obj1);
obj2.a = 3
console.log(obj.a);//1
console.log(obj2.a);//3
上面两种方式对于只有简单属性的对象而言,可以实现深拷贝,但是对象的属性也为对象时就无能为力
第三种:最终版
function deepClone(obj){
let cloneObj;
if(typeof obj === 'object'){
if(obj === null){
//若为null,直接赋值
cloneObj = null
}else if(obj instanceof Date){
//Date类型
cloneObj = new Date(obj)
}else if(obj instanceof RegExp){
//RegExp类型
cloneObj = RegExp(obj)
}else if(Array.isArray(obj)){
//Array类型
cloneObj = []
for(let key in obj){
cloneObj.push(arguments.callee(obj[key]))
}
}else{
//Object类型
cloneObj = {}
for(let key in obj){
cloneObj[key] = arguments.callee(obj[key])
}
}
}else{
//否则为基本类型
cloneObj = obj
}
return cloneObj
}
var obj = {
func:deepClone,
arr:[1,2,3,{a:{b:1}}],
obj:{
name:'obj.obj',
age:23
},
str:'11',
date:new Date(),
reg:new RegExp()
}
let obj2 = deepClone(obj)
obj2.obj.name = 'obj2.obj'
console.log('obj:',obj.obj.name);//obj: obj.obj
console.log('obj2:',obj2.obj.name);//obj2: obj2.obj
特别注意,在拷贝函数中,需要判断是否为特别对象类型(Date、RegExp等),需要重新创建再赋值。数组也需要创建空数组再进行操作。
OK,以上就是深拷贝的实现方法。
本人菜鸟一枚,本帖仅为学习之余记录贴,如有错误的地方望不吝赐教。
最后,感谢您的阅读!