JS 原生深拷贝的终极方案——structuredClone

在 JavaScript 开发中,对象的深拷贝一直是一个经典痛点。从 JSON.parse(JSON.stringify()) 的 hack,到 Lodash 的 _.cloneDeep(),开发者们一直在找一个既可靠又高效的深拷贝。2022 年,structuredClone 作为全局 API 正式进入所有主流浏览器和 Node.js,终结了这场漫长的探索。

1. 基本用法

javascript 复制代码
const original = {
  name: 'Alice',
  scores: [95, 87, 92],
  metadata: {
    createdAt: new Date(),
    pattern: /hello/gi,
  },
};

const cloned = structuredClone(original);

cloned.scores.push(100);
console.log(original.scores); // [95, 87, 92] --- 互不影响
console.log(cloned.metadata.createdAt instanceof Date); // true --- 类型保留
console.log(cloned.metadata.pattern instanceof RegExp); // true

一行代码,深拷贝完成。没有序列化/反序列化的中间步骤,没有第三方依赖。

Transfer 参数

structuredClone 的第二个参数支持 transfer 选项,这是它相比其他深拷贝方案的独特能力:

javascript 复制代码
const buffer = new ArrayBuffer(1024 * 1024 * 100); // 100MB

// 传统克隆:复制 100MB 内存
const cloned = structuredClone(buffer);

// Transfer:所有权转移,零拷贝
const transferred = structuredClone(buffer, { transfer: [buffer] });

console.log(buffer.byteLength); // 0 
console.log(transferred.byteLength); // 104857600

Transfer 的本质是所有权转移(ownership transfer),不是复制。底层只是修改了内存区域的归属指针,时间复杂度 O(1)。

为什么要这样设计?

核心原因是性能。如果要把 100MB 的视频帧数据发给 Web Worker 处理:

javascript 复制代码
// 方式 1:复制(慢,瞬间多占 100MB 内存)
worker.postMessage(buffer);

// 方式 2:转移(瞬间完成,内存不增长)
worker.postMessage(buffer, [buffer]);
// 但此后主线程不能再用这个 buffer 了

Transfer 的代价是:原始变量变成一个"空壳"(规范称为 detached / neutered)。这是一个权衡------用"独占访问权"换来了"零拷贝性能"。

2. 支持/行为

这是 structuredClone 最大的优势所在。

  • 支持:普通对象、数组、Date、Map、Set、RegExp、ArrayBuffer、TypedArray、Blob、File、BigInt、循环引用等
  • 不支持/会报 DataCloneError(典型):
    • function,函数闭包绑定了执行上下文,无法序列化
    • WeakMap/WeakSet,弱引用语义不允许枚举
    • Proxy(Vue reactive 就在这)
    • DOM Nodes,绑定了渲染树,跨上下文无意义
  • 保留对象图结构:可保留"同一引用关系"和循环结构
关键:原型链丢失
javascript 复制代码
class Vector {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  magnitude() {
    return Math.sqrt(this.x ** 2 + this.y ** 2);
  }
}

const v = new Vector(3, 4);
const cloned = structuredClone(v);

console.log(cloned.x); // 3
console.log(cloned.magnitude); // undefined 
console.log(Object.getPrototypeOf(cloned) === Vector.prototype); // false
console.log(Object.getPrototypeOf(cloned) === Object.prototype); // true

structuredClone 克隆的是数据,不是行为

对比 JSON.parse(JSON.stringify()) 支持/行为

  • 支持:普通对象、数组、字符串、数字、布尔、null
  • 会丢失/变化:
    • undefined(对象属性会被丢弃)
    • function(丢弃)
    • symbol(丢弃)
    • Date -> 字符串(ISO)
    • Map/Set -> 通常变空对象或丢信息
    • RegExp -> 普通对象/空对象
    • NaN/Infinity/-Infinity -> null
    • BigInt -> 直接报错(TypeError)
  • 循环引用:直接报错 Converting circular structure to JSON
循环引用

这是 structuredClone 相比 JSON 方案的核心优势之一:

javascript 复制代码
const obj = { name: 'root' };
obj.self = obj;
obj.children = [{ parent: obj }];

// JSON 方案直接爆炸
// JSON.stringify(obj); // TypeError: Converting circular structure to JSON

// structuredClone 优雅处理
const cloned = structuredClone(obj);
console.log(cloned.self === cloned); // true
console.log(cloned.children[0].parent === cloned); // true
console.log(cloned !== obj); // true 

算法内部维护了一个 (source, clone) 的映射表。当遍历到已经克隆过的对象时,直接返回映射表中的克隆体,从而:

  1. 避免无限递归
  2. 保持引用拓扑结构的一致性

3. 底层原理

JSON 方案底层原理

JSON.stringify(ECMAScript JSON 序列化算法)做的事:

  1. 遍历对象可序列化属性
  2. 处理 toJSON(如 Date 会先转字符串)
  3. 生成 JSON 文本(UTF-16 JS 字符串)

JSON.parse 做的事:

  1. 词法/语法解析 JSON 文本
  2. 构建全新 JS 对象树
  3. 可选 reviver 二次转换

本质:文本协议往返

structuredClone 底层原理

基于 HTML 标准里的 Structured Clone Algorithm(浏览器/运行时实现):

  1. 从源对象图开始遍历
  2. 维护"已访问对象表"(解决循环与共享引用
  3. 按对象类型走不同克隆分支(Map/Set/Date/TypedArray 等)
  4. 生成克隆对象图并重建引用关系
  5. 可选 transfer 列表:对可转移对象"转移所有权"(原对象 detach)

本质:对象图语义复制(不是文本)。

4. 一句话总结

1、JSON.parse(JSON.stringify(obj)):本质是先序列化成 JSON 文本,再解析回对象,是"文本中转"。

2、structuredClone(obj):本质是按结构化克隆算法直接复制内存对象图,不是 JSON 文本中转。

相关推荐
前端 贾公子1 小时前
响应式系统基础:依赖追踪的基础 —— 发布订阅模式(前端应用最广的设计模式)上
javascript·vue.js
索西引擎1 小时前
【理论】TypeScript 函数重载:从 Vue 3 defineEmits 说起的类型安全实践
前端·typescript
女生也可以敲代码2 小时前
2026前端面试题精选:大厂高频考点与标准答案
前端
Jinuss2 小时前
代码质量管理工具-SonarQube
前端·代码规范
gCode Teacher 格码致知2 小时前
Javascript提高:使用canvas绘制一个绚丽的按钮-由Deepseek产生
javascript·css·css3
ZFSS2 小时前
WebExtrator 网页渲染与内容提取 API 使用指南
前端·人工智能·ai·ai编程
M ? A2 小时前
VuReact:Vue转React的增量编译利器
前端·vue.js·后端·react.js·面试·开源·vureact
小四的小六2 小时前
WebView安全防护实战:从XSS到中间人攻击,我的踩坑与防御总结
javascript·webview
csj502 小时前
前端基础之《React(9)—React组件》
前端·react.js