structuredClone:原生深拷贝终于来了
structuredClone:原生深拷贝终于来了
深拷贝是 JS 的经典面试题,也是真实需求。之前写过一篇手写深拷贝的文章讲原理,这次聊聊 2022 年落地的原生方案 structuredClone,以及它能不能替代各种老办法。
老办法各自的问题
// 1. 展开运算符:只有一层
const copy = { ...obj } // obj.nested 还是引用
// 2. JSON 序列化:丢数据
JSON.parse(JSON.stringify({
date: new Date(), // 变成字符串
reg: /abc/g, // 变成 {}
fn: () => {}, // 直接消失
undef: undefined, // 直接消失
big: 10n, // 抛 TypeError
// 循环引用 -> 抛 TypeError: Converting circular structure
}))
JSON 方案胜在简单,但限制太多:类型丢失、undefined 消失、循环引用爆炸。lodash 的 cloneDeep 功能全,但为一个方法引整个库不划算。
structuredClone 上手
const original = {
date: new Date(),
set: new Set([1, 2]),
map: new Map([['a', 1]]),
nested: { arr: [1, 2, 3] },
}
original.self = original // 循环引用
const copy = structuredClone(original)
copy.nested.arr.push(4)
console.log(original.nested.arr) // [1, 2, 3] 不受影响
console.log(copy.date instanceof Date) // true,类型保留
console.log(copy.self === copy) // true,循环引用正确指向新对象
一行调用,类型保留,循环引用也正确处理。浏览器(2022 年起)和 Node 17+ 都有。
能力边界:不是所有东西都能拷
structuredClone 用的是结构化克隆算法(和 postMessage 传数据同一套),它拷贝的是"数据",不是"活的对象":
- ❌ 函数:包括方法,直接抛 DataCloneError
- ❌ DOM 节点:不能拷
- ❌ 原型链和类实例:拷完变成普通对象,
copy instanceof MyClass是 false - ❌ 属性描述符、getter/setter:只拷值,不拷行为
- ✅ Date、RegExp、Map、Set、ArrayBuffer、TypedArray、Blob、File、ImageData 等都支持
所以它的定位很清晰:纯数据的深拷贝。Vue/React 里 clone state、IndexedDB 存取、Web Worker 传参,都是它的主场。带方法的类实例、需要保留原型的对象,还是得想别的办法。
性能对比
小对象上 JSON 方案和 structuredClone 差别不大(都是微秒级)。大对象(几百 KB 的嵌套结构)实测 structuredClone 更快,而且不用先序列化成字符串再过一遍 parse,内存占用也更优。
// 简单 benchmark 一下(Node 环境)
const data = { /* 大对象 */ }
console.time('json')
JSON.parse(JSON.stringify(data))
console.timeEnd('json')
console.time('clone')
structuredClone(data)
console.timeEnd('clone')
小结
| 方案 | 嵌套 | 特殊类型 | 循环引用 | 类实例 |
|---|---|---|---|---|
| 展开运算符 | ❌ 浅 | 保留引用 | ❌ | 原型保留(浅) |
| JSON 序列化 | ✅ | ❌ 丢 | ❌ 抛错 | ❌ |
| structuredClone | ✅ | ✅ 大部分 | ✅ | ❌ 变普通对象 |
| lodash.cloneDeep | ✅ | ✅ | ✅ | 保留 |
日常首选 structuredClone,遇到类实例再上 cloneDeep。原生能力的完善让"手写深拷贝"越来越像一道纯粹的面试题——理解原理依然有价值,但生产代码里交给平台就好。
看到这里了?如果这篇文章帮到过你
请我喝瓶可乐就好 🥤

评论0 条
还没有评论,来抢沙发~