直接用 JSON.parse(JSON.stringify(obj)) 处理含循环引用的对象会抛出 TypeError,因 JSON 标准不支持循环结构;应改用 structuredClone()、手写 WeakMap 递归克隆或 Lodash 的 _.cloneDeep()。

直接用 JSON.parse(JSON.stringify(obj)) 处理含循环引用的对象,会立刻抛出 TypeError: Converting circular structure to JSON ——这不是程序 bug,而是 JSON 标准本身不允许循环结构。规避的关键不是“绕过报错”,而是换掉不支持该场景的工具。
用 structuredClone() 替代 JSON 方法
现代环境(Chrome 98+、Firefox 94+、Node.js 17.0+)已原生支持 structuredClone(),它专为解决这类问题设计:
- 自动识别并正确重建循环引用,克隆后
clone.self === clone依然成立 - 保留 Date、RegExp、Map、Set、BigInt 等类型,不转成空对象或字符串
- 一行代码搞定:
const copy = structuredClone(original); - 注意:不复制函数体(只保留引用)、不支持 DOM 节点和某些特殊内置对象
手写递归 + WeakMap 记录已拷贝对象
当需要兼容旧环境,或必须保留函数、undefined、Symbol 等 JSON 无法处理的类型时,手动实现更可控:
- 用
WeakMap缓存原始对象 → 克隆对象的映射,遇到重复引用直接返回已有副本 - 对 Array、Date、RegExp、Set、Map 等类型做单独构造,避免调用默认
{} - 函数类型可选择原样赋值(
typeof obj === 'function' ? obj : ...),不触发递归 - 示例核心逻辑:
if (seen.has(obj)) return seen.get(obj); seen.set(obj, clone);
引入成熟第三方库(如 Lodash)
项目已依赖 Lodash 或追求开箱即用的稳定性,_.cloneDeep() 是经过长期验证的选择:
- 内部已内置循环引用检测与跳过机制,不会栈溢出
- 支持大部分 JS 内置类型,包括稀疏数组、类数组对象等边界情况
- 若需保留函数,可配合
customizer参数定制行为:_.cloneDeep(obj, (val) => typeof val === 'function' ? val : undefined) - 代价是增加约 20–30KB 的包体积,纯前端小项目需权衡
不推荐的“修复”思路
试图给 JSON.stringify 加包装器来“捕获错误再重试”,本质无效:
- JSON 格式规范无循环引用语法,强行序列化必然失败,没有中间态可挽救
- 第三方库如
flatted或circular-json本质是改写序列化格式(加入$ref),结果不再是标准 JSON,前后端互操作风险高 - 降级到
try/catch后再切 deepCopy 逻辑,只是避免崩溃,不能补回丢失的数据


















