真正决定递归深拷贝可靠性的关键是正确处理边界情况:需用weakmap解决循环引用,单独处理date/regexp/map/set/arraybuffer等内置类型,覆盖不可枚举属性和symbol键,并保留原型链。

手动写递归深拷贝,真正决定可靠性的不是“能不能复制对象”,而是“哪些值没被正确处理”。很多看似能跑通的代码,一遇到真实业务数据就出错——比如 Date 变成字符串、Map 里键值全丢了、函数直接消失、或者对象一嵌套三层就卡死。这些都不是偶然,是边界情况没兜住。
循环引用必须用 WeakMap 拦截
当一个对象的属性指向自身,或 A→B→A 构成环时,纯递归会无限调用直到栈溢出。不能靠 try/catch 或深度计数来“防”,得从结构上切断路径:
- 每次进入递归前,先查 weakMap.has(obj),存在就直接返回缓存结果
- 创建新对象后(如 new Map() 或 {}),立刻 weakMap.set(obj, result)
- 必须用 WeakMap,不是 Map:它不阻止垃圾回收,也不会造成内存泄漏,且键只能是对象,天然适配引用判重
特殊内置类型不能靠 for...in 或 JSON
Date、RegExp、Map、Set、ArrayBuffer 这些类型,既不是普通对象,也不能用属性遍历复制。它们有专属构造逻辑:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- Date:用 new Date(obj) 或 new Date(obj.getTime()),保留时间戳和时区行为
- RegExp:用 new RegExp(obj.source, obj.flags),避免 obj.toString() 丢失 global/multiline 等信息
- Map / Set:不能用展开或 Object.assign;要遍历 obj.entries() 或 obj.values(),并递归拷贝每个键和值
- ArrayBuffer / TypedArray:需用 obj.slice(0) 或 new obj.constructor(obj),否则共享底层内存
不可枚举属性和 Symbol 键容易被忽略
for...in 和 Object.keys() 都只返回可枚举自有属性,但真实对象中常含不可枚举字段(如 node_modules 里某些库用 defineProperty 设置的私有状态)或 Symbol 键:
- 用 Object.getOwnPropertyNames(obj) 获取所有字符串键(含不可枚举)
- 用 Object.getOwnPropertySymbols(obj) 获取所有 Symbol 键
- 对每个键,用 Object.getOwnPropertyDescriptor(obj, key) 拿到完整描述符(含 getter/setter、writable、configurable),再用 Object.defineProperty 复制到新对象
原型链要保留但不继承原型上的属性
深拷贝的目标是复制“实例值”,不是模拟继承。但丢掉原型,instanceof 和方法调用就失效了:
- 新建对象时,用 Object.create(Object.getPrototypeOf(obj)) 而不是空对象 {}
- 不要用 for...in 遍历,它会把原型链上的可枚举属性也抄进来
- 判断属性是否属于实例,统一用 obj.hasOwnProperty(key) 或 Object.prototype.hasOwnProperty.call(obj, key)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










