简介
对于深浅拷贝的概念来自于 JS 存储数据的方式,在 JS 里面有两种数据类型:基本类型和复杂类型;
- 基本类型没有深浅拷贝的概念,因为基本类型的存储在栈内存中,在赋值的时候生成一个栈值,且互相不影响;
- 复杂类型产生了深浅拷贝的概念,因为复杂类型的值存储在堆内存中,然后变量存储的时候一个指向堆内存的地址的变量值;
在复杂类型赋值的时候,不做任何操作,赋值的是栈内存中的变量值指向真实对象地址的指向链条值(类似于图书馆每本书的索引号),这种赋值操作叫做浅拷贝;
如果我们赋值的时候,想要一个新的一模一样的对象,那么这种赋值操作叫做深拷贝;
- 简单数据类型存储图
- 复杂数据类型存储图
如何实现?
浅拷贝:直接赋值就行
深拷贝:根据对象的属性值的数据类型一个一个的进行新的生成然后赋值;
JSON序列化实现
在 JS 中复杂类型都存在自己的序列化方法,支持序列化,通过 JSON.stringify() 序列复杂类型的时候会触发它的序列化方法。然后通过 JSON.parse() 把这个序列化后的字符串还原成一个对象。一序一转在新的地址生成了新的对象;
function cloneDeep(obj){
return JSON.parse(JSON.stringify(obj));
}
但是因为对象的序列化方法实现各不一样,在解析的时候并不能完全还原一些类型。
能正确处理的情况:
- Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。
不能处理的情况:
- function
- 循环引用(json 序列化不能控制复制的层次)
- Maps,Sets,EegExps,Dates,ArrayBuffers等其他内置对象序列化可能存在问题,如 Date 序列化后在反序列化回来就变成字符串了,而不是 Date 对象。
- NaN,Infinity,精确的浮点数
循环递归
循环对象的属性值,处理特殊情况,数组,循环引用,处理特殊类型等等;
// 简单的深拷贝
function cloneDeep(obj) {
const cloneObj = {};
for(let key in obj) { // 判断key 是不是当前对象的属性
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloneObj[obj] = obj[key];
}
}
}
// 循环递归拷贝
function cloneDeep(obj) {
const cloneObj = {};
for(let key in obj) { // 判断key 是不是当前对象的属性
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (isObject(obj[key])) {
cloneObj[obj] = cloneDeep(obj[key])
} else {
cloneObj[obj] = obj[key];
}
}
}
}
// 兼容数组
function cloneDeep(obj) {
if (typeof obj === 'object') {
const cloneObj = Array.isArray(obj) ? [] : {};
for(let key in obj) { // 判断key 是不是当前对象的属性
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (isObject(obj[key])) {
cloneObj[obj] = cloneDeep(obj[key])
} else {
cloneObj[obj] = obj[key];
}
}
}
} else {
return obj;
}
}
// 解决循环引用,通过 map 存储每次的对象来,然后查询解决
// 检查map中有无克隆过的对象
// 有 - 直接返回
// 没有 - 将当前对象作为key,克隆对象作为value进行存储
// 继续克隆
function cloneDeep(obj) {
const map = new Map();
if (typeof obj === 'object') {
const cloneObj = Array.isArray(obj) ? [] : {};
if (map.get(obj)) {
return map.get(obj);
}
map.set(obj, cloneObj);
for(let key in obj) { // 判断key 是不是当前对象的属性
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (isObject(obj[key])) {
cloneObj[obj] = cloneDeep(obj[key])
} else {
cloneObj[obj] = obj[key];
}
}
}
} else {
return obj;
}
}
更多的实现参考,这篇文章,写的很详细:juejin.cn/post/684490…