递归克隆复杂嵌套业务模型需逐层判型、切断引用链、处理循环引用、保留原型链并过滤DOM等非法属性。需用WeakMap缓存已克隆对象,对Date/RegExp等调用构造器,Map/Set遍历键值克隆,自定义类优先用原constructor创建实例,并主动跳过DOM节点及谨慎处理getter。

递归克隆复杂嵌套的业务模型,核心是逐层判断类型、递归复制、切断引用链。不能直接用 JSON.parse(JSON.stringify()),它会丢函数、Symbol、undefined、Date、RegExp、Map/Set,也无法处理循环引用。
识别并分类每层数据类型
每次进入递归前,先判断当前值是否为 null、原始类型(string/number/boolean/bigint/symbol/undefined)、Array、Date、RegExp、Map、Set 或普通对象。不同类型走不同复制逻辑:
- 原始类型和 null 直接返回(不可变,无需克隆)
- Date、RegExp 等内置对象用对应构造器新建实例(
new Date(obj)、new RegExp(obj)) - Array 和普通对象(
obj.constructor === Object)进入深层递归分支 - Map/Set 需遍历键值对,分别克隆 key 和 value
用 WeakMap 处理循环引用
业务模型常含父子双向引用或配置项复用,不处理会导致栈溢出或无限递归。在递归函数外层传入一个 WeakMap 缓存已克隆过的源对象及其副本:
- 每次递归前查 WeakMap:若已存在该源对象的克隆结果,直接返回缓存值
- 首次处理时,先新建空对象(
Array.isArray(obj) ? [] : {}),立即存入 WeakMap,再遍历属性赋值 - WeakMap 能自动垃圾回收,不阻止源对象释放,适合长期运行的业务系统
保留原型链与构造器信息
业务模型常基于 class 实例化(如 class User { constructor(name) { this.name = name; } }),仅用 {} 会丢失原型方法。应优先使用原构造器创建新实例:
立即学习“Java免费学习笔记(深入)”;
- 检查
obj.constructor !== Object && obj.constructor !== Array - 对自定义类实例,用
Object.assign(new obj.constructor(), obj)或调用其自有克隆方法(如有) - 若需完全保形,可结合
Object.getPrototypeOf(obj)设置新对象原型(但注意兼容性)
避开 DOM 和不可枚举属性陷阱
真实业务模型可能混入 DOM 节点(如表单元素引用)、不可枚举属性(Object.defineProperty 定义)、getter/setter。递归中需主动过滤:
- 遇到
nodeType属性(如obj.nodeType === 1)直接跳过或抛错提示,DOM 不该进深克隆流程 - 用
Object.getOwnPropertyNames()+Object.getOwnPropertyDescriptor()获取所有自有属性(含不可枚举) - 对 getter 属性,可选择执行取值后克隆,或保留 getter 函数本身(需深克隆函数体,通常建议浅拷贝函数引用)


















