工业级深拷贝库的核心是稳、可扩展、避坑:用weakmap解决循环引用,分类型精准处理内置对象,还原元信息与原型链,并支持插件式策略扩展。

手写工业级深拷贝库,核心不是“写得全”,而是“稳得住、扩得开、不踩坑”。它必须能扛住真实项目里的各种边界情况:循环引用、Date/RegExp/Map/Set、Symbol键、不可枚举属性、原型链,还得留出扩展口子。光靠 JSON.parse(JSON.stringify(obj)) 或简单递归,上线后迟早出事。
用 WeakMap 解决循环引用
循环引用(比如 obj.a = obj)会让朴素递归无限调用,栈溢出。关键不是“检测到就报错”,而是“提前缓存、命中即返”:
- 每次进入对象前,先查
WeakMap缓存里有没有该对象的拷贝结果 - 有就直接返回,避免重复遍历和死循环
- 用
WeakMap而非Map:键只能是对象,且不阻止 GC,内存更安全 - 缓存结构是
cache.set(source, clone),后续遇到同一 source 就return cache.get(source)
分类型精准处理内置对象
不能对所有引用类型都用 {...obj} 或 Object.assign,不同构造器要走不同路径:
-
Date / RegExp:用构造函数重建,
new Date(obj.getTime())、new RegExp(obj.source, obj.flags) -
ArrayBuffer / TypedArray:用
slice(0)或new obj.constructor(obj)复制底层二进制 -
Map / Set:遍历
entries()或values(),key 和 value 都要递归深拷(Map 的 key 可能也是对象) - Function:默认保留原函数(闭包无法真正复制),可配置为跳过或浅拷贝;真需要行为克隆(如绑定新 this),应由用户显式传参控制
还原元信息与原型链
普通遍历(for...in、Object.keys)会丢掉 Symbol 键、不可枚举属性(如 constructor)、getter/setter 和原型:
- 用
Object.getOwnPropertyNames(obj)+Object.getOwnPropertySymbols(obj)拿全自有属性名 - 用
Object.getOwnPropertyDescriptors(obj)获取完整描述符(含enumerable、writable、get/set) - 用
Object.defineProperties(clone, descriptors)一次性还原所有特性 - 用
Object.getPrototypeOf(obj)获取原型,再用Object.setPrototypeOf(clone, protoClone)设置(注意null原型需单独判断)
支持插件式策略扩展
硬编码所有类型判断难维护、难测试、难协作。工业级做法是抽象出处理器接口:
- 每个处理器暴露两个方法:
canHandle(obj)判断是否接管,clone(obj, cache, cloneFn)执行拷贝 - 内置处理器按优先级注册(如 Date > Map > Object),避免冲突
- 用户可随时注册自定义类型处理器(比如支持 protobuf、BigInt64Array 或业务专属类)
- 主函数只做调度:遍历处理器列表,找到第一个
canHandle返回 true 的,交由它处理











