手动实现深拷贝需用weakmap拦截循环引用,首次访问对象时占位并缓存,再次访问直接返回副本;基础类型直接返回,特殊对象分类处理;cache必须显式传递避免污染;现代环境优先使用structuredclone。

手动实现深拷贝时,循环引用会导致递归无限深入,最终栈溢出。解决的关键不是阻止递归,而是“提前拦截”——在每次进入递归前,查一下这个对象是不是已经拷贝过了;如果已存在,就直接返回对应的新副本,不再往下走。
用 WeakMap 记录旧对象到新对象的映射
WeakMap 是首选容器,因为它的键是对象引用,且不阻止垃圾回收,安全又轻量。每次递归开始前,先用原对象作 key 查 WeakMap:
- 查到了,说明该对象已在拷贝过程中生成过副本,直接返回它
- 没查到,就新建空对象(或数组等),立刻存入 WeakMap,再继续递归处理其属性
- 这样即使 a → b → a 形成环,第二次遇到 a 时也能命中缓存,跳出递归链
基础类型和特殊对象要分类处理
不是所有值都需要进 WeakMap 或递归。需按类型分层判断:
- null、undefined、string、number、boolean、symbol、bigint:直接返回,不进缓存
- Date、RegExp、ArrayBuffer、TypedArray:用 new Date(obj) 等构造新实例,不递归内部,但需单独存入 WeakMap(避免重复构造)
- Map / Set:新建实例后,递归拷贝其键和值(键也可能是对象,同样走 WeakMap 检查)
- 普通对象或数组:先占位(new Object() 或 []),存进 WeakMap,再遍历赋值
递归调用必须传入缓存实例,不能用默认空对象
常见错误是把缓存写成函数默认参数:function deepClone(obj, cache = {})。这会导致多次调用共用同一个 cache,跨调用污染数据。正确写法是:
- 默认设为
cache = null - 首次调用时初始化:
if (cache == null) cache = new WeakMap() - 后续递归都显式传入当前 cache 实例
现代环境可优先用 structuredClone
如果运行环境支持(Chrome 98+、Node.js 17.0+),structuredClone(obj) 是最简方案。它内置循环检测和类型支持(Date、Map、Set、Blob 等),一行代码搞定,且语义准确、性能可靠。手动实现更适合学习原理或兼容老环境。











