开发的时候碰上个带 Map、Set 甚至循环引用的深拷贝,手写递归或上 JSON 轮子都有点力不从心。但浏览器其实早就内置了一个好用的解法——structuredClone。这个 API 直接基于“结构化克隆算法”,跟 postMessage 底层那套逻辑一脉相承。 它到底能对付什么类型的对象?这得
使用structuredClone深拷贝包含Map、Set及循环引用的对象
作者:技术拾光
时间:2026-07-30
浏览:385
文章目录
开发的时候碰上个带
它到底能对付什么类型的对象?这得先摸清楚边界。
Map、Set 甚至循环引用的深拷贝,手写递归或上 JSON 轮子都有点力不从心。但浏览器其实早就内置了一个好用的解法——structuredClone。这个 API 直接基于“结构化克隆算法”,跟 postMessage 底层那套逻辑一脉相承。
它到底能对付什么类型的对象?这得先摸清楚边界。
支持类型一览
日常会用到的主流类型基本都在支持名单里: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 里的循环引用也束手无策)。实用工具推荐
电子包浆
使用 ›绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
图片压缩
使用 ›选择画质和输出格式,预览压缩前后的大小。
相关文章