javascript递归深拷贝需用weakmap缓存已处理对象以解决循环引用:先查cache.get(obj),存在则直接返回;否则创建新拷贝并cache.set(obj, copy),再递归处理属性或元素,特殊类型如map、set需先缓存空实例再填充。

JavaScript 递归实现深拷贝时,循环引用会导致栈溢出或无限递归。核心解法是用 WeakMap 记录已拷贝过的对象(包括对象、数组、Map、Set 等),遇到重复引用直接返回已有拷贝结果。
用 WeakMap 缓存已处理的对象
WeakMap 的键必须是对象,且不阻止垃圾回收,适合做引用追踪。每次进入拷贝逻辑前,先查 WeakMap:如果当前源对象已存在对应拷贝,直接返回它,跳过递归。
- 初始化一个
cache = new WeakMap()作为外部闭包变量或传入参数 - 拷贝前调用
cache.get(obj),若返回值存在,直接返回该值 - 创建新拷贝后,立即执行
cache.set(obj, copy)
区分可遍历类型与基础类型
只对对象、数组、Date、RegExp、Map、Set、ArrayBuffer 等需要深拷贝的类型做缓存和递归;对 null、undefined、原始类型(string/number/boolean/symbol/bigint)和函数,直接返回或按需处理(函数通常不拷贝,保持引用)。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
obj === null || typeof obj !== 'object'→ 直接返回(原始值) -
obj instanceof Date→new Date(obj.getTime()) -
obj instanceof RegExp→new RegExp(obj) - 普通对象/数组 → 先缓存占位,再遍历属性/元素递归拷贝
特殊内置对象的处理顺序很重要
Map 和 Set 内部可能包含循环引用,必须在创建空实例后立刻缓存,再逐个 set 或 add 拷贝后的键值/元素,否则递归中再次遇到该 Map/Set 会重新尝试拷贝,破坏缓存逻辑。
- Map:先
const mapCopy = new Map(),cache.set(original, mapCopy),再遍历original.entries(),对每对[key, value]递归拷贝后mapCopy.set(keyCopy, valueCopy) - Set 同理:先新建 Set 并缓存,再遍历添加拷贝后的元素
避免使用 JSON.parse(JSON.stringify())
这种写法无法处理函数、undefined、Symbol、Date、RegExp、BigInt、循环引用,且会丢失原型链和稀疏数组结构,不是真正的深拷贝,仅适用于纯数据对象的简单场景。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










