JavaScript ES6中的深拷贝和浅拷贝
前两天在做Vue项目时,遇到了涉及到对象展开运算符"..."是深拷贝还是浅拷贝的问题,在问答里看到了这篇回答,由于无法转载,在这里记录一下。
先来了解下,浅拷贝和深拷贝各自的含义:
数据可以分为两大类:一类是基本数据,一类是引用数据;而数据存储位置又可分为栈和堆。
基本数据是存储在栈中的,深拷贝和浅拷贝操作的都是真实的数据,所以没有区别。
故而,深拷贝和浅拷贝主要是针对引用数据来说的。
引用数据,相当于是在栈中存储了一个地址,这个地址指向堆中的真实数据,
浅拷贝,相当于把栈中的地址给了一个新的对象,新对象和旧对象指向的都是堆中同一个数据,所以无论哪个对象发生变化都会影响堆中的这个数据;
深拷贝,是在堆中重新开辟一片空间,生成了一个新的数据区域,这样新对象指向的是新的数据,旧对象指向的是旧的数据,所以这两个数据区无论谁发生变化都不会影响另一个。
浅拷贝
浅拷贝是指创建一个新的对象,把这个对象的原始属性精确拷贝一份,
如果是基本类型就拷贝基本类型的值,
如果是引用类型,拷贝的就是内存地址,如果其中一个引用类型改变了值,那么就会影响另一个对象
深拷贝
深拷贝是将一个对象从内存中完成拷贝出来,在内存中开辟一个新的区域存放对象,并且两者不会相互影响
看图理解
例子:
赋值
var person1 = {
name: '小明',
age: 18,
sex: '男',
}
var xiaoHong = person1; // 这里只是将引用变量person1给了xiaoHong
xiaoHong.name = '小红';
console.log(person1) // 输出:{name: "小红", age: 18, sex: "男"}
console.log(xiaoHong)// 输出:{name: "小红", age: 18, sex: "男"}
由上面的例子,很清楚的看到:当xiaoHong的name属性值变化了之后,person1的name属性值也发生变化了,可以使用浅拷贝来解决这个问题
浅拷贝—对象展开运算符
var person1 = {
name: '小明',
age: 18,
sex: '男',
}
var xiaoHong = {...person1}
xiaoHong.name = '小红';
console.log(person1) // 输出: {name: "小明", age: 18, sex: "男"}
console.log(xiaoHong)// 输出: {name: "小红", age: 18, sex: "男"}
哎,用对象展开符的xiaoHong的name属性变化了之后,person1的name属性没有变化呀!难道对象扩展符是深拷贝?别急,往下看。
再看下面的例子,对象中包含着第二层数据对象。
var person1 = {
name: '小明',
age: 18,
sex: '男',
hobby: {
music: '伤感类歌曲',
like: '吃饭、睡觉、打游戏',
}
}
var xiaoHong = {...person1}
xiaoHong.name = '小红';
xiaoHong.hobby.music = '小清新歌曲';
console.log(person1)
// 输出: {name: "小明", age: 18, sex: "男", hobby: {music: "小清新歌曲",like: "吃饭、睡觉、打游戏"}}
console.log(xiaoHong)
// 输出: {name: "小红", age: 18, sex: "男", hobby: {music: "小清新歌曲",like: "吃饭、睡觉、打游戏"}}
看上面代码片段,对比新旧两个对象的输出:
你发现对象展开运算符并不是深拷贝,而是浅拷贝。
我感觉可以这么理解:
展开运算符使用的对象内部如果是基本数据类型,就是深拷贝;
展开运算符使用的对象内部如果是引用数据类型,那就是浅拷贝;
这种时候,就需要深拷贝解决问题了
深拷贝 — JSON.parse(JSON.stringify(object))
let obj = {
a: 1,
b: {
c: 2,
d: 3,
},
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)