一、理解拷贝
拷贝就像我们平常cv一些文件、代码 复制了一份新的就叫拷贝。 换成js代码就变成了基本数据类型和引用数据类型的拷贝。
在JavaScript中我们定义的简单数据类型会存到栈(stack)内存中,包括引用类型的指针,而这个指针指向的地方是在堆(heap)内存中。也通常理解为,简单数据类型存在栈中,引用数据类型存在堆中
二、基本数据类型拷贝
基本数据类型没什么好说的,通过一个变量赋值给一个变量就已经拷贝完成了
let a = 1
let b = a
把a赋值给b之后 再去操作a并不会影响b 你可以理解为 a 和b 是两个不同的区域,但都是存在栈内存里。
let a = 1
let b = a
a=5
console.log(b); //1
console.log(a); //5
三、浅拷贝(shallow clone)
浅拷贝只能拷贝复杂数据类型的指针,并不能改变复杂数据类型的地址,只能拷贝外层,并不能彻底拷贝,例如数组中还有数组(对象),(准确来说是外层引用数据类型)
对象浅拷贝
(1)依次赋值
优点:数量较少的时候使用方便、简单,缺点:遇到对象或数组键比较多时,操作不方便
let obj = {
a: {name: 'abc'},
b: 2
}
let newObj = {}
newObj.a=obj.a
newObj.b=obj.b
(2)for…in 遍历
let obj = {
a: {name: 'abc'},
b: 2
}
let newObj = {}
for (const key in obj) {
newObj[key]=obj[key]
}
(3)Object.assign(目标对象,要拷贝的对象)
let obj = {
a:{name:'奥特曼'},
b:2
}
let newObj= {}
Object.assign(newObj,obj)
(4)展开运算符
let obj = {
a: {name: 'abc'},
b: 2
}
let newObj = {}
newObj = {...obj}
缺陷:只能拷贝外层不能拷贝内层
浅拷贝只能拷贝最外层的引用地址,并不能拷贝内层的引用地址
修改最内层的数据 另一个也会改变,因为指针指向的的都是同一个内存地址
newObj.a.name='bcd'
console.log(obj); //a: {name: "bcd"}, b: 2
console.log(newObj); //a: {name: "bcd"}, b: 2
数组浅拷贝
(1)依次赋值
let arr = [1,{name:'abc'}]
let newArr = []
newArr[0]=arr[0]
newArr[1]=arr[1]
(2)展开运算符
let arr = [1,{name:'abc'}]
let newArr = [...arr]
(3)slice
let arr = [1,{name:'abc'}]
let newArr = arr.slice(0)
(4)map
let arr = [1,{name:'abc'}]
let newArr = arr.map(it=>it)
同样修改数组中内存的数据 另一个数组的内存数据也会改变
arr[1].name='bcd'
console.log(arr);// 1, {name: "bcd"}
console.log(newArr);// 1, {name: "bcd"}
四、深拷贝
深拷贝:不光外层的引用地址该变了 内层的引用数据类型也发生改变
对象数组深拷贝
(1)递归
obj = {
a: {name: 'abc'},
b: 2
}
function fn(obj) {
let newObj = {}
// 如果不是对象直接返回
if(typeof obj !=='object'){
return '不是一个对象'
}else {
// 是对象就对每一项进行遍历
for (const key in obj) {
// 判断内部是否为对象 如果不是对象就进行递归
if(typeof obj[key] ==='object'){
newObj[key]=fn(obj[key])
}else{
newObj[key]=obj[key]
}
}
}
return newObj
}
优化
obj = {
a: { name: 'abc' },
b: 2
}
function fn(obj) {
let newObj = {}
if(typeof obj !=='object') return '不是一个对象'
for (const key in obj) {
typeof obj[key] ==='object'? newObj[key]=fn(obj[key]): newObj[key]=obj[key]
}
return newObj
}
let newObj = fn(obj)
这时候在修改对象里面的引用类型时 就不会影响另一个对象
newObj.a.name='bcd'
console.log(obj);
console.log(newObj);
(2)JSON.parse(JSON.stringify())
let newObj = JSON.parse(JSON.stringify(obj))
这个深拷贝我们经常会在网上看到,但也不是没有缺陷
obj = {
a:NaN,
b: undefined,
c:new Date(),
d:new RegExp(/\d/),
d:new Error(),
e:Infinity
}
console.log(obj);
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj);
JSON.parse(JSON.stringify())拷贝后的缺陷:
NaN ===》null
undefined ===》 空
时间戳 ===》字符串时间
错误信息 ===》 空对象
Infinity ===》null