深拷贝与浅拷贝
深浅拷贝的出现,其实是引用类型的特性导致的;若只是简单数据类型,便都是深拷贝;
由于复杂数据类型的特点:复杂数据类型的赋值,并不是将数据赋给对方,而是将一个引用地址赋给对方,这就会导致,两者的地址,指向的都是同一个堆区,那么此时随便一个变量修改堆区的内容,另一个变量也会随之改变;
例如:
let a = 1
let b = a
b = 2
console.log(a, b)
let obj1 = {
a: 1,
b: 'zs',
c: undefined,
d: { name: '嘻嘻' },
e: [1, 9],
func: () => {}
}
let obj2 = obj1
obj2.d.name = '哈哈'
obj2.e[0] = 6
obj2.a = 02
console.log(obj1, obj2)
打印结果:
- 作为简单数据类型的
a和b,赋值后不存在一个改变,另一个也改变的情况; 而obj1和obj2,则是obj1(或者obj2)中的内容,一个改变,另一个也会随之改变
深拷贝要解决的就是上述问题,让复杂数据类型被拷贝后,也可以像简单数据类型一样。
浅拷贝
-
对象浅拷贝解决方法一:展开运算符
let obj1 = { a: 1, b: 'zs', c: undefined, d: { name: '嘻嘻' }, e: [1, 9], func: () => {} } let obj2 = { ...obj1 } obj2.d.name = '哈哈' obj2.e[0] = 6 obj2.a = 2 console.log('obj1:', obj1, 'obj2:', obj2)可以看到,
扩展运算符也可以实现第一级元素的深拷贝,第二级以上就只能实现浅拷贝 -
对象浅拷贝解决方法二:object.assign方法
let obj1 = {
a: 1,
b: 'zs',
c: undefined,
d: { name: '嘻嘻' },
e: [1, 9],
func: () => {}
}
let obj2 = Object.assign({}, obj1)
obj2.d.name = '哈哈'
obj2.e[0] = 6
obj2.a = 2
console.log('obj1:', obj1, 'obj2:', obj2)
深拷贝
-
对象深拷贝解决方法一:JSON
let obj1 = { a: 1, b: 'zs', c: undefined, d: { name: '嘻嘻' }, e: [1, 9], func: () => {} } let obj2 = JSON.parse(JSON.stringify(obj1)) obj2.d.name = '哈哈' obj2.e[0] = 6 obj2.a = 2 console.log('obj1:', obj1, 'obj2:', obj2)打印的结果为:
可以看到,JSON的方式,可以实现深拷贝,但是,有缺点,
缺点:数据类型为undefined、symbol和function不能拷贝 -
对象深拷贝解决方法二:递归(比较完美的方案)
let obj1 = {
a: 1,
b: 'zs',
c: undefined,
d: { name: '嘻嘻' },
e: [1, 9],
func: () => {}
}
function cloneDeep (data) {
// 判断传进来的是数组还是对象,分别返回对应的空数组和空对象,作为容器
const newData = Array.isArray(data) ? [] : {}
for (key in data) {
if (data[key] && typeof data[key] === 'object') {
// 进入此处,data[key] 不是对象就是数组,那就调用cloneDeep方法,进行递归!直到data[key] 为一个简单数据类型为止。
newData[key] = cloneDeep(data[key])
} else {
// 来到此处,说明 data[key] 就是简单数据类型,不用进行递归,直接赋值。
newData[key] = data[key]
}
}
return newData
}
let obj2 = cloneDeep(obj1)
obj2.d.name = '哈哈'
obj2.e[0] = 6
obj2.a = 2
console.log('obj1:', obj1, 'obj2:', obj2)
数组的浅拷贝,和上面一样,但是数组又多出两个方法
-
数组方法——slice
let arr1 = [1, 2, undefined, function () {}, { name: 'ls' }] let arr2 = arr1.slice() arr2[0] = 8 arr2[4].name = '李四' console.log('arr1:', arr1, 'arr2:', arr2)
同样的,
数组的slice方法 也可以实现第一级元素的深拷贝,第二级以上就只能实现浅拷贝
-
数组方法——concat
let arr1 = [1, 2, undefined, function () {}, { name: 'ls' }] let arr2 = arr1.concat() arr2[0] = 8 arr2[4].name = '李四' console.log('arr1:', arr1, 'arr2:', arr2)同样的,
数组的concat方法 也可以实现第一级元素的深拷贝,第二级以上就只能实现浅拷贝
数组递归方法(数组深拷贝 比较完美的方案)
代码逻辑和上面对象递归一样
let arr1 = [1, 2, undefined, function () {}, { name: 'ls' }]
function arrCloneDeep (data) {
let newArray = Array.isArray(data) ? [] : {}
for (key in data) {
if (data[key] && typeof data[key] === 'object') {
newArray[key] = arrCloneDeep(data[key])
} else {
newArray[key] = data[key]
}
}
return newArray
}
let arr2 = arrCloneDeep(arr1)
arr2[0] = 8
arr2[4].name = '李四'
console.log('arr1:', arr1, 'arr2:', arr2)
如上图,完美的深拷贝!
总结
对象浅拷贝解决方案:
- JSON转换法
- 缺点:数据类型为
function和undefined时,无法复制
- 缺点:数据类型为
- 递归 1. 比较完美解决方案
数组浅拷贝解决方案:
-
JSON转换法
- 缺点:数据类型为
function和undefined时,无法复制
- 缺点:数据类型为
-
递归
- 比较完美解决方案