优先使用structuredClone()进行深拷贝,它原生支持Date、RegExp、Map、Set等并处理循环引用;不支持时需手写递归,对各类内置对象显式重建,函数则保持引用。

直接用 structuredClone() 是最稳妥的方式,它原生支持 Date、RegExp、Map、Set、ArrayBuffer 等类型,还能处理循环引用。如果环境不支持(比如旧版 Node 或某些浏览器),就得手写递归,并对特殊类型单独处理。
优先用 structuredClone()
现代浏览器和 Node.js 17+ 均已支持:
-
structuredClone({ date: new Date(), reg: /abc/g, map: new Map([[1, 'a']]) })能完整保留类型和值 - 不丢失时间精度、正则 flags、Map 键值对结构
- 自动识别并安全处理循环引用,不会报错
- 但函数(
Function)仍会被忽略——这是规范行为,不是 bug
Date 和 RegExp 必须显式重建
手写深拷贝时,不能靠 JSON.stringify 或简单遍历,它们会把 Date 变成字符串、RegExp 变成空对象:
- 遇到
obj instanceof Date→ 返回new Date(obj.getTime())或new Date(obj) - 遇到
obj instanceof RegExp→ 返回new RegExp(obj.source, obj.flags) - 注意:
new RegExp(obj)也行,但显式传source和flags更清晰、兼容性更好
其他内置类型也要分类处理
除了 Date 和 RegExp,Map、Set、TypedArray 等同样不能直接递归赋值:
-
obj instanceof Map→ 创建新 Map,遍历obj.entries(),递归拷贝每个 key 和 value -
obj instanceof Set→ 创建新 Set,遍历obj.values(),递归拷贝每个值 -
ArrayBuffer或Uint8Array等 → 使用slice()或构造函数复制缓冲区 - 普通对象和数组继续递归;基本类型(string/number/boolean/null/undefined/symbol/BigInt)直接返回
别忘了类型判断要准确
仅靠 typeof 无法区分数组、Date、RegExp、Map 等,它们都返回 "object":
- 统一用
Object.prototype.toString.call(obj)判断,例如:[object Date]、[object RegExp]、[object Map]、[object Array] - 再结合
instanceof或构造函数检查,增强可读性和健壮性 - 函数类型建议直接返回原引用(即浅拷贝函数),因为真正“克隆”函数在 JS 中不可行且无实际意义


















