structuredClone vs lodash.cloneDeep:核心差异与选型指南

做前端这么多年,JSON.parse(JSON.stringify(obj)) 这行代码你一定不陌生。

它简单、好用、随处可见------但也处处是坑:

  • 函数?没了。
  • undefined?没了。
  • Symbol?没了。
  • 循环引用?直接报错。
  • Date、RegExp、Map、Set?统统变成你不认识的样子。

2022 年,浏览器终于推出了原生的深拷贝 API ------ structuredClone() 。它一度被称作"JSON 深拷贝的终结者"。

但现实是:它并不是 lodash.cloneDeep 的替代品,而是互补品。

一个处理纯数据,一个兜底带行为的数据。今天就把两者的边界讲清楚。

一、核心能力对比

根据 greatfrontend 链接1 链接2 整理的对比表:

值 / 场景 structuredClone Lodash cloneDeep
嵌套对象 / 数组 ✅ 克隆 ✅ 克隆
Date ✅ 保留 Date ✅ 保留 Date
RegExp ✅ 保留 RegExp ✅ 保留 RegExp
Map / Set ✅ 克隆 ✅ 克隆
循环引用 ✅ 支持 ✅ 支持
函数 / 方法 ❌ 抛 DataCloneError ✅ 按引用保留
Symbol 键 ❌ 不克隆 ✅ 克隆
类实例原型链 ❌ 丢失,变普通对象 ✅ 保留原型链
BigInt ✅ 保留 ✅ 保留
instanceof 检查 ❌ 类实例克隆后 instanceof 失效 ✅ 保持类实例身份

二、关键差异详解

1. 函数处理:最本质的分水岭

structuredClone 遇到函数直接抛异常:

js

scss 复制代码
structuredClone({ fn: () => {} }); 
// ❌ DataCloneError: () => {} could not be cloned.

cloneDeep 将函数按引用保留 :

js

ini 复制代码
const obj = { fn: () => console.log('hello') };
const clone = _.cloneDeep(obj);
clone.fn === obj.fn; // true(函数本身是同一引用)

这意味着:如果你的数据模型包含方法或回调函数,structuredClone 完全不可用,只能选 cloneDeep。

2. 原型链:类实例的命运不同

structuredClone 返回普通对象,原型链被剥离 :

js

javascript 复制代码
class User {
  constructor(name) { this.name = name; }
  greet() { return `Hi, ${this.name}`; }
}

const u = new User('张三');
const cloned = structuredClone(u);

cloned instanceof User; // ❌ false
cloned.greet;           // ❌ undefined

cloneDeep 保留原型链 :

js

ini 复制代码
const cloned = _.cloneDeep(u);
cloned instanceof User; // ✅ true
cloned.greet();         // ✅ 'Hi, 张三'

但注意:两者都不会重新执行构造函数 ,只在构造时生效的不变量不会在克隆对象上重新应用 。

3. Symbol 键的处理

structuredClone 不克隆 Symbol 键的属性 ,cloneDeep 则可以 。

js

ini 复制代码
const sym = Symbol('key');
const obj = { [sym]: 'value' };

structuredClone(obj); // ❌ { }
_.cloneDeep(obj);     // ✅ { [sym]: 'value' }

三、性能对比

性能数据存在一定争议,取决于测试场景和对象规模:

  • 多份来源认为 structuredClone 在大型 / 复杂对象上更快 ,因为底层是原生 C++ 实现,且与 postMessage、IndexedDB 共用同一套高效算法 。
  • 也有 benchmark 显示在特定数组拷贝场景下 cloneDeep 略快 (680K vs 590K ops/sec),但这可能受测试用例和运行环境影响。

务实结论 :性能不应作为首要选型依据。两者在大多数业务场景下都够快,差距不会成为瓶颈。功能匹配度才是决定因素。

四、选型决策树

根据 greatfrontend 的建议,按顺序判断 :

text

javascript 复制代码
1. 对象包含函数、方法,或类实例的原型链必须保留?
   → 是:使用 lodash.cloneDeep
   → 否:继续

2. 对象包含 Date、Map、Set、RegExp、TypedArray、ArrayBuffer 或循环引用?
   → 是:使用 structuredClone(原生、无依赖)
   → 否:继续

3. 所有值都是严格 JSON 安全的(字符串、有限数字、布尔、null、纯对象/数组)?
   → 是:两者均可,优先 structuredClone 作为默认

五、一句话总结

structuredClone 是现代 JavaScript 深拷贝的默认选择;只有当函数、Symbol 或类实例原型链成为硬性需求时,才需要退回 lodash.cloneDeep 。

如果你的项目是纯数据流(无函数、无类实例),structuredClone 是零依赖、更简洁、且与浏览器底层机制一致的首选。如果数据结构中嵌入了行为(方法、回调),cloneDeep 仍是当前最稳妥的兜底方案。

相关推荐
Java后端的Ai之路2 小时前
CAS并发控制-从CPU指令到企业级乐观锁
vue.js·人工智能·python·cas·乐观锁
程序员大阳5 小时前
副队长JavaScript教程(50)-DOM:网页开发的起点
javascript·dom·es·节点·概念·document·关系
香芋芋圆9 小时前
iD-editor 详解|OSM官方在线编辑器,基于D3.js的轻量地图编辑器
开发语言·javascript·编辑器
晗夜未央11 小时前
任务队列反复失败,先分清重试、阻塞和结果待核对
开发语言·javascript
jktantan12 小时前
每个节点 41 字节:把一棵百万节点树拆进 14 个 typed array
前端·javascript·vue.js·算法·typescript
我命由我1234513 小时前
访客系统形式(有用户概念、无用户概念)
java·开发语言·前端·javascript·python·产品运营·产品经理
程序员大阳13 小时前
副队长JavaScript教程(65)-详解JSON:轻量级数据交换格式
javascript·json·序列化·反序列化·stringify·parse
小大宇14 小时前
Vue知识点-ElementPlus-06 单选+多选+下拉+日期选择
前端·vue.js·elementui
paopaokaka_luck15 小时前
基于springboot+vue的益暖微光公益服务平台(DeepSeek公益助手、善款信息追溯、志愿服务积分闭环、ECharts运营分析、活动状态自动流转)
java·前端·javascript·spring boot·mysql·架构·echarts
栀椩15 小时前
Electron 内嵌页面跨域问题(CORS/PNA)解决全记录
前端·javascript·electron