# 深拷贝
# 介绍
我们知道在 javascript 中有八种数据类型:其中 number,string,undefined,null,boolean,symbol 和 bigint 为基本数据类型,而 object 为复杂数据类型
# 存储
- 对于基本数据类型,其值直接存储于栈中。
- 对于复杂数据类型,其值存储于堆中,而栈中只存储堆中的地址。这样做的好处有:
- 节省内存空间:存储在堆中的对象可以通过栈内的引用被访问和操作,意味着对象可以在不同的上下文中被共享和引用,从而节省内存。
- 垃圾回收:堆内存中的对象不再被引用时,垃圾回收机制就会自动进行回收,从而避免了内存泄漏和资源浪费。
- 动态分配内存:堆内存允许对象动态的增长和缩小,因此可以根据需要灵活地修改对象的结构和内容,使得 js 对象可以轻松地扩展以适应不同的应用需求。
# 深拷贝
而深拷贝和浅拷贝就是对于复杂数据类型 object 来说的,当拷贝了对象的一层(即堆的引用)时,就称之为浅拷贝,当拷贝了对象的两层(堆中创建新的对象)时,就称之为深拷贝。
# 实现方案
JSON.parse(JSON.stringify(obj))
,第一种方法是使用 JSON 方法,但是这个方法有一些限制:JSON 序列化时函数会被转为 null,正则表达式会被转为空对象。递归实现:
```javascript
function deepCopy(obj, parent = null) {
// 创建一个新对象
let result = {};
let keys = Object.keys(obj),
key = null,
temp = null,
_parent = parent;
// 该字段有父级则需要追溯该字段的父级
while (_parent) {
// 如果该字段引用了它的父级则为循环引用
if (_parent.originalParent === obj) {
// 循环引用直接返回同级的新对象
return _parent.currentParent;
}
_parent = _parent.parent;
}
for (let i = 0; i < keys.length; i++) {
key = keys[i];
temp = obj[key];
// 如果字段的值也是一个对象
if (temp && typeof temp === 'object') {
// 递归执行深拷贝 将同级的待拷贝对象与新对象传递给 parent 方便追溯循环引用
result[key] = DeepCopy(temp, {
originalParent: obj,
currentParent: result,
parent: parent
});
} else {
result[key] = temp;
}
}
return result;
}
```
MessageChannel 实现深克隆:由于浏览器不能将一个函数正确的复制到另一个线程中,所以不能支持函数的深克隆。
function deepCopy(obj) {
return new Promise((resolve) => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}
deepCopy(obj).then((copy) => {// 异步的
let copyObj = copy;
console.log(copyObj, obj)
console.log(copyObj == obj)
});
H5 新增 structuredClone
结构化克隆解决了该 JSON.stringify () 技术的许多(尽管不是全部)缺点。结构化克隆可以处理循环依赖,支持许多内置数据类型,并且更健壮且速度更快。
但是,它仍然有一些限制:
原型:如果你使用 structuredClone () 类实例,你将获得一个普通对象作为返回值,因为结构化克隆会丢弃对象的原型链。
函数:如果你的对象包含函数,它们将被悄悄丢弃。
不可克隆:有些值不是结构化可克隆的,尤其是 Error、 DOM 节点 和 Function。尝试这样做将引发 DataCloneError 异常。
属性描述符:setter 和 getter (以及类似元数据的功能) 不会被复制。例如,如果使用属性描述符将对象标记为只读,则复制后的对象中是可读写 (默认配置)。
RegExp:RegExp 对象的 lastIndex 字段不会保留。
# 浅拷贝实现方案
Object.assign({},obj)
,第二方法是使用 Object.assign ()const obj1 = {...obj2}
,使用扩展运算符