一、区别
和原数据是否指向同一对象 | 基本数据类型的第一层数据 | 原数据中的子对象 | |
---|---|---|---|
赋值 | 是 | 修改会使原数据一同修改 | 修改会使原数据一同修改 |
浅拷贝 | 否 | 修改不会使原数据一同修改 | 修改会使原数据一同修改 |
深拷贝 | 否 | 修改不会使原数据一同修改 | 修改不会使原数据一同修改 |
二、数据类型
js的数据类型划分方式为 基本数据类型(Undefined,Null,Boolean,Number、String)
和 引用数据类型Object(包含 function、Array、Date)
对于基本数据类型来说,不存在赋值、深浅拷贝这个问题,每次赋值都相当于是一次深拷贝
三、赋值、浅拷贝、深拷贝
已知:基本数据类型的数据存储在栈内存中,引用数据类型的数据存储在堆内存。
假设: [1,[2,3],4]
是一个数组,存放在堆内存中,该数据在堆内存的地址为127.0.0.1
则栈内存中的存储数据为a = 127.0.0.1
赋值:增加了一个指针指向已存在的内存地址
修改a后,b会随之改变
var a = [1,[2,3],4];//将[1,[2,3],4] 存放到堆中, a = 127.0.0.1
var b= a;//相当于 b = 127.0.0.1
a[0]='c'//修改数组
a[1][0]='d'
console.log(a)//a = 127.0.0.1 指向 ["c",["d", 3], 4]
console.log(b)//b = 127.0.0.1 指向 ["c",["d", 3], 4]
浅拷贝:申请了一个新的内存,拷贝对象中第一层的基本数据,不拷贝对象中的子对象
既然对于基本数据类型来说,每次赋值都相当于是一次深拷贝,那是不是可以通过循环遍历对象的基本数据,将他的值赋给新对象
var a = [1,[2,3],4];
var b= [];
for (var i in a){
b[i] = a[i]
}
a[0] = 5
a[1][1] = 6
console.log(a)//[5, [2, 6], 4]
console.log(b)//[1, [2, 6], 4]
注意:a[1]
是一个数组,假设堆内存中存储[2,3]
这个数组的地址是127.0.0.2
,则循环到b[1] = a[1]
这一步时,实际上执行的是b[1] = 127.0.0.2
深拷贝:申请了一个新的内存,并复制其全部内容
既然循环遍历只能拷贝到第一层的基本数据类型,子对象无法拷贝到,那我们可以通过判断每个元素的数据类型,然后递归调用循环遍历,这就是一个简单的深拷贝了
var a = [1,[2,3],4];
function copy(a){
let res = [];
for (var i in a){
if (typeof a[i]==="object"){
//typeof 判断不准确,建议用Object.prototype.toString.call
res[i] = copy(a[i])//递归调用copy()
}else{
res[i] = a[i]
}
}
return res;
}
var b = copy(a);
a[0] = 5
a[1][1] = 6
console.log(a)//[5, [2,6], 4]
console.log(b)//[1, [2,3], 4]