JavaScript存储引用数据(对象)都是存地址的,存放在堆内存中的对象,在栈内存中存的是一个指针,这个指针指向堆内存一个位置。再从堆内存中取得所需的数据。
深拷贝:对数据进行拷贝之后,修改拷贝之后的数据原数据不会发生改变,新数据在堆内存中重新开辟出来一个新的地址,两层数据保存的地址是独立的,所以数据互不影响。
浅拷贝:对数据进行拷贝之后,修改拷贝之后的数据原数据也会改变,新数据和原数据在栈内存中以指针的形式存储,并且同时指向了堆内存中同一个地址,所以修改拷贝之后的数据原数据也会改变,业务场景中浅拷贝经常会发生数据冲突
浅拷贝原理图
下面以举例子的形式,对比深拷贝和浅拷贝,以便于理解的更详细
对简单数据类型进行赋值修改
let c = 2
let b = c
b+=5
console.log(c,b) // 2,7
现象:简单数据类型之间的值没有互相影响
对引用数据类型进行拷贝
let a = { name: 'ggc' }
let b = a
b.name = 'lnw'
console.log(a, b)
let arr = [1,2,3]
let arr2 = arr
arr2.push(99)
console.log(arr,arr2)
现象:当修改了声明的引用类型b和arr2时,原始的数据也发生了改变,这属于浅拷贝。
解释分析:浅拷贝只会发生在引用类型身上,对于引用类型如果之进行简单的赋值,只会赋值指向堆内存的指针,这种称为浅拷贝。而深拷贝就是完全拷贝一个引用类型,为不是地址指针。
那我们在拷贝原始数据时肯定是不希望对原始数据进行修改的,因为影响到原来的数据了,这种情况怎么办呢?
深拷贝就上场了
1.通过JSON.stringify和JSON.parse
可以深拷贝的数组和对象,但是不能拷贝函数,可以进行对象或者数组的嵌套拷贝。
缺点:无法实现对对象中方法的深拷贝
由此可以看出函数是没有办法拷贝的
2. 利用扩展运算符进行深拷贝
缺点:无法对对象里面嵌套的对象进行深拷贝,相当于只是对一层引用对象进行深拷贝
3.手写递归深拷贝函数
//使用递归实现深拷贝
function deepClone(obj) {
//判断拷贝的obj是对象还是数组
var objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") { //obj不能为空,并且是对象或者是数组 因为null也是object
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") { //obj里面属性值不为空并且还是对象,进行深度拷贝
objClone[key] = deepClone(obj[key]); //递归进行深度的拷贝
} else {
objClone[key] = obj[key]; //直接拷贝
}
}
}
}
return objClone;
}
举例:
var arr = {
name: '浪漫主义码农',
age: 20,
adress: ['jiangxi', 'changsha'],
friends: {
friend1: '张三',
friend2: '李四'
},
fun: function(){
console.log("我是" + this.name + "的对象")
}
}
var brr = deepClone(arr)
brr.name = '法外狂徒张三'
brr.adress[0] = '长沙'
console.log("arr为", arr)
arr.fun()
console.log("brr为", brr)
brr.fun()
//使用递归实现深拷贝
function deepClone(obj) {
//判断拷贝的obj是对象还是数组
var objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") { //obj不能为空,并且是对象或者是数组 因为null也是object
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") { //obj里面属性值不为空并且还是对象,进行深度拷贝
objClone[key] = deepClone(obj[key]); //递归进行深度的拷贝
} else {
objClone[key] = obj[key]; //直接拷贝
}
}
}
}
return objClone;
}