优先使用 structuredClone()——它原生支持循环引用及 Date、RegExp、Map、Set 等复杂类型,现代浏览器和 Node.js v17+ 已兼容;不支持函数、Symbol、DOM 节点等;老环境可用 WeakMap 递归兜底;禁用 JSON 方案;Lodash cloneDeep 是成熟备选。

直接用 structuredClone(),它是目前最可靠、最简洁的解法。
优先用 structuredClone() —— 原生支持循环引用
现代浏览器(Chrome 98+、Firefox 94+、Safari 15.4+)和 Node.js(v17.0+)已原生支持 structuredClone()。它专为处理循环引用、嵌套对象、Date、RegExp、Map、Set、ArrayBuffer 等复杂类型设计,内部自动维护对象映射表,遇到重复引用时直接复用已克隆实例,彻底避免栈溢出。
- ✅ 支持循环引用(如
obj.a = obj) - ✅ 正确克隆 Date、RegExp、Map、Set、TypedArray、Blob 等内置对象
- ❌ 不克隆函数、Symbol、DOM 节点、Error 对象、属性描述符(如
writable、enumerable) - ⚠️ 注意:若需兼容老环境(如 IE 或旧版 Node),不能直接使用
示例:
const original = { name: 'config' };
original.self = original; // 循环引用
const copy = structuredClone(original); // ✅ 成功,copy.self === copy
手写递归 + WeakMap —— 兼容性兜底方案
当必须支持 structuredClone() 不可用的环境时,可实现一个带缓存的递归深拷贝函数。核心是用 WeakMap 记录「原始对象 → 已克隆对象」的映射,每次进入对象前先查表,命中则直接返回,切断递归链。
- 需显式判断类型:Array、Object、Date、RegExp、Map、Set 等分别构造新实例
- 对 null 和基本类型直接返回;对函数可选择跳过或浅拷贝(通常不复制)
- 避免用
obj.constructor构造,因某些对象(如 Array subclass)可能行为异常,推荐用Array.isArray()、obj instanceof Date等安全判断
简版示意:
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
let clone;
if (obj instanceof Date) clone = new Date(obj);
else if (obj instanceof RegExp) clone = new RegExp(obj);
else if (obj instanceof Map) clone = new Map([...obj]);
else if (obj instanceof Set) clone = new Set([...obj]);
else if (Array.isArray(obj)) clone = obj.map(item => deepClone(item, seen));
else clone = { ...obj }; // 简单对象,不处理 getter/setter 和不可枚举属性
seen.set(obj, clone);
return clone;
}
慎用 JSON.parse(JSON.stringify()) —— 看似简单实则高危
这个方法在含循环引用的配置对象上会直接报错:TypeError: Converting circular structure to JSON。即使对象“看起来没循环”,只要内部有 Date、RegExp、undefined、函数、Symbol 或 BigInt,结果就会被静默丢弃或转成空对象/字符串,导致配置丢失或语义错误。
- ❌ 遇循环引用立即崩溃
- ❌ 过滤掉 undefined、Symbol、函数、NaN、Infinity
- ❌ Date 变成字符串,RegExp 变成空对象,Map/Set 完全消失
- ❌ 无法保留原型链、不可枚举属性、getter/setter
仅适用于纯数据、无任何特殊类型的临时调试场景,绝不用于生产环境的配置拷贝。
考虑成熟工具库 —— 快速落地不踩坑
如果项目已引入 Lodash,_.cloneDeep() 是经过大量实战验证的选择。它支持循环引用、大部分内置类型,且对边界情况(如稀疏数组、自定义类实例)处理更稳健。
- ✅ 支持循环引用、Date、RegExp、Map、Set、TypedArray
- ✅ 可选传入自定义克隆逻辑(通过
customizer) - ❌ 不保留 Symbol 属性、函数、属性描述符(与 structuredClone 一致)
- ⚠️ 注意包体积,若仅需深拷贝,引入整个 Lodash 可能得不偿失
示例:
import { cloneDeep } from 'lodash-es';
const copy = cloneDeep(originalConfig); // 安全、稳定、省心











