深浅拷贝
浅拷贝复制了什么?
- 语义: 复制对象或数组的引用地址, 而不是其内部值;
- 结果: 新容器与旧容器共享同一批子对象, 修改子对象会同时可见;
- 常用手段: 扩展运算符
...、Object.assign、Array.prototype.slice、Array.prototype.concat;
const arr1 = [{}];
const arr2 = [...arr1];
arr1[0].foo = "bar";
console.log(arr2[0]); // { foo: 'bar' }; 子对象仍共享
- 集合互操作:
...依赖默认迭代器, 可在数组、类型化数组、Map、Set之间转换, 详见 迭代器;
JSON 方案做深拷贝有什么限制?
- 做法:
JSON.parse(JSON.stringify(obj)); - 优点: 简单, 无需递归;
- 限制: 会丢失
undefined、函数与 symbol, 不能处理循环引用, 且会改变Date、Map、Set等类型的语义;
const obj1 = { a: 1, b: { c: 2 } };
const copy = JSON.parse(JSON.stringify(obj1));
structuredClone 适合什么场景?
- 做法: 调用内建
structuredClone(value), 支持循环引用与多数内建类型; - 限制: 旧运行环境可能不支持, 且不能克隆函数、DOM 节点等不可序列化值;
如何手写能处理循环引用的深拷贝?
- 思路: 递归复制, 用
WeakMap记录已复制的对象, 再次遇到时直接返回对应副本;
const deepClone = (obj, hash = new WeakMap()) => {
if (typeof obj !== "object" || obj === null) return obj;
if (hash.has(obj)) return hash.get(obj);
const newObj = Array.isArray(obj) ? [] : {};
hash.set(obj, newObj);
for (const key in obj) {
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
newObj[key] = deepClone(obj[key], hash);
}
return newObj;
};
- 循环引用:
obj1.obj2 = obj2; obj2.obj1 = obj1也能正确复制, 因为WeakMap缓存了中间结果;