JavaScript深拷贝需识别并单独处理Date、RegExp、Map、Set等特殊对象,用对应构造器创建新实例并复制状态,同时用WeakMap避免循环引用,现代环境推荐使用structuredClone()。

JavaScript 中深拷贝 Date、RegExp 等特殊对象,不能依赖 JSON.parse(JSON.stringify())(它会丢失类型信息),也不能只递归普通对象/数组。关键在于识别构造器类型,用对应构造函数创建新实例,并复制其内部状态。
识别并单独处理常见内置特殊对象
除了 Object 和 Array,Date、RegExp、Map、Set、TypedArray、Error 等都有自己的构造逻辑。深拷贝时需显式判断 instanceof 或 Object.prototype.toString.call(),再针对性克隆:
-
Date:直接用
new Date(original.getTime())或new Date(original.toString()) -
RegExp:用
new RegExp(original.source, original.flags)(注意 global、ignoreCase 等 flags) - Map/Set:新建实例,遍历原结构,递归拷贝键和值(Map 的键也可能是引用类型,需深拷贝)
-
TypedArray(如 Uint8Array):用
original.constructor(original)或new original.constructor(original.buffer, original.byteOffset, original.length)
避免循环引用导致栈溢出
特殊对象本身可能参与循环引用(比如某个 Date 被多个对象引用,或 RegExp 存在闭包上下文)。深拷贝函数必须维护一个 WeakMap 缓存已处理的源对象 → 目标对象映射:
- 每次进入拷贝前,先查缓存;命中则直接返回已生成副本
- 对 Date/RegExp 这类不可变对象,缓存不是必须,但为统一逻辑建议保留
- Map/Set/Array/Object 等可变容器必须用缓存,否则递归会无限展开
保持原型链与不可枚举属性
某些场景需要保留原始对象的原型(例如自定义继承 Date 的子类),此时不能简单用字面量重建:
立即学习“Java免费学习笔记(深入)”;
- 用
Object.create(Object.getPrototypeOf(original))创建新对象 - 再用
Object.getOwnPropertyDescriptors()获取所有属性(含不可枚举、getter/setter) - 对每个 descriptor,若值是对象,递归深拷贝;否则直接赋值
- RegExp 和 Date 默认不带自定义属性,但若手动添加了不可枚举属性(如
Object.defineProperty(reg, 'meta', {value: 1, enumerable: false})),也要一并拷贝
推荐轻量方案:基于 structuredClone 的现代解法
Chrome 98+、Firefox 99+、Node.js 17.0+ 已支持 structuredClone(),它原生支持 Date、RegExp、Map、Set、ArrayBuffer、TypedArray、Error 等,并自动处理循环引用:
- 直接调用
structuredClone(obj)即可,无需手动判断类型 - 不支持的环境(如旧版 Safari)可用 clone-deep 或 lodash.cloneDeep 等成熟库
- 注意:
structuredClone不克隆函数、undefined、Symbol、Promise、DOM 节点等,这点与手写逻辑一致


















