文章目录

JavaScript structuredClone深拷贝与JSON序列化的区别

发布于 2026-05-02 16:13:40 · 浏览 4 次 · 评论 0 条

JavaScript structuredClone深拷贝与JSON序列化的区别

在JavaScript开发中,经常需要复制一个对象。如果直接赋值,只是复制了引用,修改新对象会影响原对象。为了得到一个完全独立的副本,我们需要“深拷贝”。目前最常用的两种方案是传统的 JSON.parse(JSON.stringify()) 和现代的 structuredClone()


1. 验证基础数据类型的拷贝

首先测试两种方法在处理简单对象时的表现。

打开浏览器的开发者工具控制台。输入以下代码并执行

const original = { name: "Alice", score: 90 };
const jsonCopy = JSON.parse(JSON.stringify(original));
const cloneCopy = structuredClone(original);

// 修改原对象
original.name = "Bob";

// 查看结果
console.log(jsonCopy.name); // 输出: "Alice"
console.log(cloneCopy.name); // 输出: "Alice"

观察输出结果,两者都成功保留了 Alice,说明在处理纯数据对象时,两者都能实现基本的深拷贝。


2. 处理特殊对象类型(Date 与 RegExp)

当对象中包含日期、正则等特殊类型时,差异就会显现。

定义一个包含 DateRegExp 的对象:

const specialObj = {
  date: new Date('2023-10-01'),
  regex: /test/gi
};

使用 JSON 序列化方法进行拷贝:

const jsonResult = JSON.parse(JSON.stringify(specialObj));
console.log(jsonResult.date);     // 输出: 字符串 "2023-10-01T00:00:00.000Z"
console.log(jsonResult.regex);    // 输出: 空对象 {}
console.log(jsonResult.date instanceof Date); // 输出: false

使用 structuredClone 方法进行拷贝:

const cloneResult = structuredClone(specialObj);
console.log(cloneResult.date);     // 输出: Date 对象
console.log(cloneResult.regex);    // 输出: RegExp 对象
console.log(cloneResult.date instanceof Date); // 输出: true

对比结果,JSON 方法把时间变成了字符串,把正则变成了空对象;而 structuredClone 完美保留了它们的原始类型。


3. 解决循环引用问题

循环引用是指对象的属性引用了对象本身,这是深拷贝中常见的“坑”。

创建一个具有循环引用的对象:

const circularObj = { name: "Loop" };
circularObj.self = circularObj; // 属性指向自己

尝试使用 JSON 序列化:

try {
  JSON.parse(JSON.stringify(circularObj));
} catch (e) {
  console.error("JSON报错:", e.message);
}
// 控制台输出: "JSON报错: Converting circular structure to JSON"

切换structuredClone

const clonedCircular = structuredClone(circularObj);
console.log(clonedCircular.self === clonedCircular); // 输出: true

注意structuredClone 成功拷贝了对象,并且新对象内部的循环引用依然指向新对象自身,没有报错。


4. 拷贝机制的底层逻辑

为了更直观地理解两者在处理复杂结构时的差异,可以参考以下流程逻辑:

graph TD A[输入对象] --> B{包含特殊类型?} B -- "Date / RegExp / Map / Set" --> C[JSON 序列化] C --> D[丢失类型
转为普通对象或字符串] B -- "Date / RegExp / Map / Set" --> E[structuredClone] E --> F[保留原始类型] A --> G{包含循环引用?} G -- 是 --> C C --> H[抛出错误] G -- 是 --> E E --> I[拷贝成功]

5. 处理不支持的数据类型(函数与 Symbol)

两者也都有无法处理的“盲区”,主要针对函数和 Symbol。

定义包含函数和 Symbol 的对象:

const funcObj = {
  id: Symbol("id"),
  sayHello: function() { console.log("Hi"); }
};

分别运行两种拷贝方式:

// JSON 序列化
const jsonFunc = JSON.parse(JSON.stringify(funcObj));
console.log(jsonFunc.id);       // 输出: undefined
console.log(jsonFunc.sayHello); // 输出: undefined

// structuredClone
try {
  structuredClone(funcObj);
} catch (e) {
  console.error("structuredClone报错:", e.message);
}
// 控制台输出: "structuredClone报错: #<Object> could not be cloned"

注意,JSON 会直接忽略函数和 Symbol(变为 undefined),而 structuredClone 会直接抛出错误。


6. 核心差异对比表

为了方便快速查阅,以下是两种方法的详细对比:

特性 JSON 序列化 structuredClone
基本类型支持 支持 支持
对象/数组 支持 支持
Date (日期) 转为字符串 保留 Date 对象
RegExp (正则) 转为空对象 {} 保留 RegExp 对象
Map / Set 转为普通对象 {} 保留 Map / Set
循环引用 抛出错误 支持拷贝
Function (函数) 忽略 (变为 undefined) 抛出错误
Symbol 忽略 (变为 undefined) 抛出错误
执行环境 极高 (所有浏览器) 较高 (现代浏览器, Node 17+)

7. 选择合适的方法

根据项目需求和数据结构,选择正确的方案:

  1. 如果数据结构简单,只包含数字、字符串、数组或普通对象,且不需要考虑极老的浏览器环境,使用 JSON.parse(JSON.stringify()) 是完全没问题的。
  2. 如果数据中包含 DateMapSetRegExp 或存在循环引用,必须使用 structuredClone()
  3. 如果数据中包含函数或 Symbol,且必须保留它们,这两种方法都无法满足需求,需要使用 Lodash 的 _.cloneDeep() 等第三方库,或者手写递归拷贝函数。

运行以下代码判断当前环境是否支持 structuredClone

if (typeof structuredClone === 'function') {
  console.log("当前环境支持 structuredClone");
} else {
  console.log("当前环境不支持 structuredClone");
}

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文