一,背景
Javascript 中的数据类型分为基本数据类型和引用数据类型两种。
基本数据类型:字符串、布尔值、数字、未定义(undefined)和 NULL。
引用数据类型:对象,对象又分为数组、Date、RegExp(正则表达式)、函数。
基本数据类型存放在栈内存(stack)中,引用数据类型会将一个十六进制的地址(指向堆中的本体)存放在栈内存里,而 其本身的值存放在堆内存(heap) 中。
而我们的深拷贝和浅拷贝都是 对于引用数据类型来说的。(对于基本数据类型没有意义,两者相同,都是复制值,严格地说是赋值。)
let a = 5;
let b = a;
b = 3;
console.log(a,b) //b = 3 a = 5
二,定义
浅拷贝:拷贝引用类型变量的地址(这样会导致原来值改变会影响复制得到值,我们复制得到的值更改也会影响原来值,这个不注意会产生很多问题)
深拷贝:完整拷贝一份对象,从堆内存里开辟一个新区域放置新对象,这时改变新对象就不会影响旧对象了
三,实例
浅拷贝:
数组和对象的赋值都是浅拷贝。下面的例子可以看出我们修改了man2,但是man1也变了。
let man1 = {name:'1'}
let man2 = man1
man2.name = "2"
console.log(man1,man2)
// {name: "2"} {name: "2"}
深拷贝:
解构赋值是深拷贝。
JSON.stringify和JSON.parse也为深拷贝,但是需要注意的是无法拷贝RegExp对象,函数和Symbol。
let arr = [1,2,3]
let arr2 = [...arr]
arr2.push(4);
console.log(arr,arr2)
//arr: [1,2,3] arr2: [1,2,3,4]
四,结构对拷贝的影响
数组forEach方法存在单层结构深拷贝,多层结构浅拷贝的情况
其实就是拷贝的第一层不是引用类型是基本类型的时候拷贝的是值,而第一层如果是对象类型的时候拷贝的则是指针。
// 单层结构
let a = [1, 2, 3]
let b = []
a.forEach(item => {
b.push(item)
})
b[0] = 0
console.log(a, b)
// [1, 2, 3] [0, 2, 3]
// 多层结构
let a = [1, 2, 3, [4, 5]]
let b = []
a.forEach(item => {
b.push(item)
})
b[3][1] = 4
console.log(a, b)
// [1, 2, 3, [4, 4]] [1, 2, 3, [4, 4]]
除了forEach,还有slice,concat,Object.assign也有这种情况。
五,深拷贝方法
处理object和array:
let newList = JSON.parse(JSON.stringify(list))
还想要处理function的话:
function deepClone(source){
//constructor 会指向基类:[] => Array(基类) {}=> Object
const targetObj = source.constructor === Array ? [] : {}
for(let keys in source){
if(source[keys] && typeof source[keys] === 'object'){
targetObj[keys] = deepClone(source[keys])
}else{
//基本数据类型则直接赋值
targetObj[keys] = source[keys];
}
}
}