深拷贝循环引用栈溢出的本质是递归无退出路径,解决关键是用WeakMap缓存已处理对象并复用副本;需按类型分层处理、显式传递缓存、优先使用structuredClone()。

深拷贝遇到循环引用时栈溢出,本质是递归调用没有退出路径。解决的关键不是禁止递归,而是让第二次碰到同一个对象时直接复用已生成的副本——靠缓存识别并拦截重复处理。
用 WeakMap 缓存原始对象与克隆体的映射
WeakMap 是最合适的缓存容器:键可以是任意对象,且不阻止垃圾回收,避免内存泄漏。
- 每次进入深拷贝函数前,先用 WeakMap.has(obj) 检查该对象是否已被处理过
- 若已存在,直接返回 cache.get(obj),跳过所有后续逻辑
- 若不存在,立即创建空结构(如
{}或[]),并调用 cache.set(obj, clone) 占位 - 再对属性逐个递归拷贝——此时即使子属性又指向原对象,也能命中缓存中断递归链
按类型分层处理,避免误缓存或漏处理
不是所有值都需要进 WeakMap 或递归。必须区分基础类型、特殊内置对象和普通引用类型:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 基础类型(string/number/boolean/null/undefined/symbol/bigint):直接返回,不参与缓存也不递归
-
Date、RegExp、ArrayBuffer、TypedArray:用构造函数新建实例(如
new Date(obj.getTime())),再存入 WeakMap - Map / Set:新建对应实例后,对其键值对分别递归拷贝(键也可能是对象,同样走 WeakMap 检查)
- 普通对象或数组:先占位存入 WeakMap,再遍历属性/元素深拷贝
缓存必须显式传递,不能依赖默认参数
常见错误是把缓存写成默认参数:function deepClone(obj, cache = new WeakMap())。这会导致多次调用共用一个 cache 实例,跨调用污染。
立即学习“Java免费学习笔记(深入)”;
- 正确做法:默认设为
cache = null - 首次调用时判断
if (cache == null) cache = new WeakMap() - 所有递归调用都显式传入当前
cache实例,确保上下文隔离
优先使用 structuredClone() 原生方案
如果你的运行环境支持(Chrome 98+、Firefox 98+、Safari 16.4+、Node.js 17.0+),structuredClone(obj) 是首选。
- 一行代码完成拷贝,自动检测并处理循环引用
- 原生支持 Date、Map、Set、Blob、File 等类型,无需手动适配
- 不支持函数、DOM 节点、Symbol —— 这些本就不该出现在可序列化数据中

















