使用structuredClone深拷贝包含Map、Set及循环引用的对象

作者:技术拾光

时间:2026-07-30

浏览:385

开发的时候碰上个带 Map、Set 甚至循环引用的深拷贝,手写递归或上 JSON 轮子都有点力不从心。但浏览器其实早就内置了一个好用的解法——structuredClone。这个 API 直接基于“结构化克隆算法”,跟 postMessage 底层那套逻辑一脉相承。 它到底能对付什么类型的对象?这得

文章目录
开发的时候碰上个带 Map、Set 甚至循环引用的深拷贝,手写递归或上 JSON 轮子都有点力不从心。但浏览器其实早就内置了一个好用的解法——structuredClone。这个 API 直接基于“结构化克隆算法”,跟 postMessage 底层那套逻辑一脉相承。 使用structuredClone深拷贝包含Map、Set及循环引用的对象 它到底能对付什么类型的对象?这得先摸清楚边界。

支持类型一览

日常会用到的主流类型基本都在支持名单里:
  • Map 和 Set —— 键值对、元素都能完整复制,顺序也老老实实保留着。
  • 循环引用 —— 比如 a.b = a 这种结构,它能自动识别并重建引用关系,既不会爆栈也不会报错,这才是它的看家本领。
  • 常见内置类型 —— Date、RegExp、部分环境下的 Error、ArrayBuffer 以及对应的视图(TypedArray、DataView)。
  • 基础类型和普通结构 —— 普通对象、数组、BigInt、Boolean、String、Number、null、undefined(不过 undefined 在某些上下文可能会被悄悄丢弃,需要注意)。

不支持的雷区

不过它也不是万能的。遇到下面这些类型,会直接抛出 DataCloneError 异常:
  • 函数、Promise、WeakMap、WeakSet。
  • 所有 DOM 节点(比如 HTMLElement 这些根本没法序列化的东西)。
  • 带不可枚举属性、访问器(getter/setter)、Symbol 键的对象 —— Symbol 键会被直接忽略。
  • Error 实例在不同浏览器的表现也不一样:Chrome 98+、Firefox 101+、Safari 15.4+ 才开始比较好的支持。

用法与错误处理

调用本身很直接,但必须记着用 try...catch 包一层。毕竟一旦遇到不支持的类型就炸了,怎么处理需要明确预期。
const original = new Map([['key', { count: 1 }]]);
original.set('self', original); // 制造一个循环引用

try {
  const cloned = structuredClone(original);
  console.log(cloned.get('self') === cloned); // true 
} catch (err) {
  if (err.name === 'DataCloneError') {
    console.error('无法克隆该对象:包含不支持的类型');
  }
}

降级方案

如果有兼容旧浏览器的需求(比如 Safari 15.4 之前的版本),可以先检测一下 structuredClone 是否可用,再用自己的降级逻辑兜底。
function safeClone(obj) {
  if (typeof structuredClone === 'function') {
    try {
      return structuredClone(obj);
    } catch {
      // fallback to JSON + manual handling for Map/Set (limited)
    }
  }
  // 降级逻辑:例如先用 JSON.stringify + JSON.parse 处理纯数据,
  // 再用自定义函数重建 Map/Set(但无法处理循环引用)
}
必须承认,目前还没有哪个通用降级方案能完整复刻 structuredClone 对循环引用 + Map/Set 并存的场景。生产环境里,还是明确浏览器支持范围更靠谱;如果实在需要覆盖更全,可以考虑成熟库(比如 lodash.cloneDeep,但要注意它对 Map/Set 里的循环引用也束手无策)。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章