JS原生深拷贝的终极方案structuredClone用法代码示例

 更新时间:2026年05月27日 11:39:40   作者:代码里的小猫咪  
structuredClone()是JavaScript中用于创建对象深拷贝的原生方法,相比JSON.parse等传统方法,它能更全面地处理复杂数据类型,这篇文章主要介绍了JS原生深拷贝的终极方案structuredClone用法的相关资料,需要的朋友可以参考下

前言

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

1. 基本用法

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 选项,这是它相比其他深拷贝方案的独特能力:

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 处理:

// 方式 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,绑定了渲染树,跨上下文无意义
  • 保留对象图结构:可保留“同一引用关系”和循环结构

关键:原型链丢失

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 方案的核心优势之一:

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) 的映射表。当遍历到已经克隆过的对象时,直接返回映射表中的克隆体,从而:

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

3. 底层原理

JSON 方案底层原理

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

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

JSON.parse 做的事:

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

本质:文本协议往返

structuredClone 底层原理

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

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

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

4. 一句话总结

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

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

附:structuredClone () 与其他克隆方法详细对比

与 JSON.parse (JSON.stringify ()) 对比

在 JavaScript 中,JSON.parse(JSON.stringify()) 是一种常用的实现深拷贝的方法,但它与 structuredClone() 存在诸多差异。

在支持的数据类型方面,JSON.parse(JSON.stringify()) 仅支持基本数据类型(如数字、字符串、布尔值)、普通对象和数组 。对于 Date 对象,它会将其转换为字符串,在反序列化后不再是 Date 对象,而是普通字符串;Map、Set、RegExp 对象会被转换为空对象;函数、undefined、symbol、Infinity、NaN 以及循环引用等都无法正确处理 。而 structuredClone() 支持的数据类型更为广泛,除了基本数据类型和普通对象、数组外,还支持 Date、RegExp、Map、Set、ArrayBuffer、TypedArrays、Blob、File、ImageData、MessagePort 等,并且能正确处理循环引用 。例如:

// JSON.parse(JSON.stringify()) 处理不支持的类型
const original1 = {
date: new Date(),
map: new Map([['key1', 'value1']]),
func: function() { return 'function' }
};
const jsonClone1 = JSON.parse(JSON.stringify(original1));
console.log(jsonClone1.date instanceof Date); // false,Date 被转为字符串
console.log(jsonClone1.map instanceof Map); // false,Map 被转为空对象
console.log(jsonClone1.func); // undefined,函数丢失
// structuredClone() 处理支持的类型
const original2 = {
date: new Date(),
map: new Map([['key1', 'value1']]),
arrayBuffer: new Uint8Array([1, 2, 3]).buffer
};
const structuredClone2 = structuredClone(original2);
console.log(structuredClone2.date instanceof Date); // true
console.log(structuredClone2.map instanceof Map); // true
console.log(structuredClone2.arrayBuffer instanceof ArrayBuffer); // true

在循环引用处理上,JSON.parse(JSON.stringify()) 无法处理对象中的循环引用,当对象存在循环引用时,调用 JSON.stringify() 会抛出 TypeError: Converting circular structure to JSON 错误 ,导致克隆失败。而 structuredClone() 可以正确处理循环引用,确保克隆后的对象包含正确的循环引用结构,不会陷入无限循环 。比如:

// JSON.parse(JSON.stringify()) 处理循环引用
let obj1 = {};
let obj2 = { ref: obj1 };
obj1.ref = obj2;
try {
const jsonClone = JSON.parse(JSON.stringify(obj1));
} catch (error) {
console.error('JSON.parse(JSON.stringify())循环引用错误:', error); // 抛出错误
}
// structuredClone() 处理循环引用
let obj3 = {};
let obj4 = { ref: obj3 };
obj3.ref = obj4;
const structuredClone3 = structuredClone(obj3);
console.log(structuredClone3.ref === structuredClone3.ref.ref); // true,循环引用处理正确

性能方面,JSON.parse(JSON.stringify()) 在处理简单的、JSON 兼容的数据结构时性能尚可,但在处理复杂对象或包含大量非 JSON 兼容类型的数据时,由于需要进行序列化和反序列化操作,效率较低 。structuredClone() 是为深度克隆设计的原生方法,内部针对复杂场景进行了优化,通常在处理复杂对象时性能更优 。通过以下测试可以明显看出两者的性能差异:

// 性能测试
const largeArray = new Array(10000).fill({ key: 'value' });
console.time('JSON.parse(JSON.stringify())');
const jsonClone = JSON.parse(JSON.stringify(largeArray));
console.timeEnd('JSON.parse(JSON.stringify())');
console.time('structuredClone');
const structuredClone4 = structuredClone(largeArray);
console.timeEnd('structuredClone');

在浏览器兼容性上,JSON.parse(JSON.stringify()) 在现代浏览器和较旧的浏览器中都有广泛支持 。而 structuredClone() 是一种较新的 API,在某些较旧的浏览器中不被支持 ,使用时需要考虑兼容性问题,可以通过检测 typeof structuredClone === 'function' 来判断浏览器是否支持该方法,不支持时可采用其他替代方案。

到此这篇关于JS原生深拷贝的终极方案structuredClone用法代码示例的文章就介绍到这了,更多相关JS原生深拷贝structuredClone内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 最全面的百度地图JavaScript离线版开发

    最全面的百度地图JavaScript离线版开发

    最全面的百度地图JavaScript离线版开发,这篇文章主要为大家详细介绍了JavaScript离线版开发的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • IE事件对象(The Internet Explorer Event Object)

    IE事件对象(The Internet Explorer Event Object)

    不同于DOM事件对象,基于Event Handler授权这种方式,IE事件对象可以用不同的方式进行访问。当一个事件Handler通过DOM 0 级的方式被授权,则这个事件对象将作为window对象的属性而存在
    2012-06-06
  • javacript获取当前屏幕大小

    javacript获取当前屏幕大小

    本文主要介绍使用javacript获取当前屏幕大小的方法,需要的朋友可以参考下。
    2016-06-06
  • 浅析我对JS延迟异步脚本的思考

    浅析我对JS延迟异步脚本的思考

    这篇文章主要介绍了浅析我对JS延迟异步脚本的思考,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • JS时间转换标准格式、时间戳转换标准格式的示例代码

    JS时间转换标准格式、时间戳转换标准格式的示例代码

    这篇文章主要介绍了JS时间转换标准格式、时间戳转换标准格式的示例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • JS写滑稽笑脸运动效果

    JS写滑稽笑脸运动效果

    这篇文章主要介绍了JS写滑稽笑脸运动效果,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • 前端实现图片懒加载的三种方案及对比详解

    前端实现图片懒加载的三种方案及对比详解

    本文深入探讨前端图片懒加载的各种实现方案,从传统的滚动监听到现代的 IntersectionObserver API,再到浏览器原生支持的 loading=lazy,帮助开发者选择最适合的技术方案,需要的朋友可以参考下
    2026-03-03
  • javascript 动态数据下的锚点错位问题解决方法

    javascript 动态数据下的锚点错位问题解决方法

    用 Javascript 实现锚点(Anchor)间平滑跳转
    2008-12-12
  • JS删除String里某个字符的方法

    JS删除String里某个字符的方法

    这篇文章主要介绍了JS删除String里某个字符的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,,需要的朋友可以参考下
    2019-06-06
  • JS实现的获取银行卡号归属地及银行卡类型操作示例

    JS实现的获取银行卡号归属地及银行卡类型操作示例

    这篇文章主要介绍了JS实现的获取银行卡号归属地及银行卡类型操作,结合实例形式分析了javascript不依赖第三方接口计算银行卡归属地相关信息操作技巧,需要的朋友可以参考下
    2019-01-01

最新评论