在了解浅拷贝和深拷贝之前,我们先了解一下js的数据类型
js的数据类型分为两种
一种是基本数据类型:字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)
一种是引用数据类型:对象(Object)、数组(Array)、函数(Function)
基本类型的变量是存放在栈内存,引用数据类型是放在堆内存中的,基本数据类型保存的是值,而引用数据类型一般保存的是对象的地址。
如果我们单纯的复制的话,可能就只是复制对象的地址,那么这就是浅拷贝,如果是克隆了对象,改变了引用对象的地址,那么就是深拷贝。
浅拷贝就是拷贝指向对象的指针
深拷贝就是克隆了对象,并重新在堆中指向了一个新的地址
只有引用数据类型才存在浅拷贝和深拷贝,基本数据类型就是赋值
上面用文字说了那么多可能还是很懵
那么举个例子吧
//浅拷贝
let obj1 = {a:1,b:{c:1}}
let obj2 = {...obj1}
obj1.b === obj2.b // 值为true,说明对象 obj1 的子对象 b 和 对象 obj2 的子对象指向同一个地址
//深拷贝
let obj1 = {a:1,b:{c:1}}
let obj3 = {..obj1,b:{...obj1.b}}
obj3.b === obj1.b // 值为false,说明对象 obj1 的子对象 b 和 对象 obj3 的子对象在堆内存中指向不同的地址
简而化之:
浅拷贝和深拷贝的区别是:看复制的子对象是否在堆内存中指向一个新的地址
那么怎么实现一个深拷贝呢?
实现思路:
- 检查类型,判断类型是否为引用类型,是就进行深拷贝,否则浅拷贝
- 使用递归
- 检查环,判断当前引用是否指向自身,避免进入死循环
- 需要忽略原型
简易版实现代码(只处理了Object和Array两种引用类型):
function deepClone(obj){
// 判断当前对象是对象还是数组
let result = Array.isArray(obj)?[]:{};
if(obj && typeof obj === "object"){
for(let key in obj){
// 判断是否为自身属性
if(obj.hasOwnProperty(key)){
if(obj[key] && typeof obj[key] === "object"){
//子元素为对象,进入递归
result[key] = deepClone(obj[key]);
}else{
result[key] = obj[key];
}
}
}
}
return result;
}
快捷深拷贝的方法
- 通过序列化和反序列化得到一个新的对象,JSON.parse(JSON.stringify(a));缺点:会忽略 Function、Symbol、undefined,造成属性丢失,如果在知道深拷贝对象的类型中不含有这些类型的话,这个方法还是相当的好用。
- 第三方工具:jquery 提供一个$.extend可以用来做深拷贝,lodash函数库提供的_.cloneDeep也可以用来做深拷贝