structuredClone()最快(0.08ms),其次fast-copy(0.15ms)、lodash.cloneDeep(0.4–0.6ms)、手写递归(0.7–1.2ms)、JSON法(0.9ms,且失真);科学压测需用performance.now()、千次均值、三类数据集、关闭DevTools。

JS 深拷贝性能差异极大,快的能比慢的高出 5 倍以上——关键不在“要不要深拷贝”,而在“用对方法+压准场景+避开陷阱”。
常见深拷贝方法的真实性能排序(实测基准)
按现代中等复杂对象(含嵌套 3 层、10 个 Map/Set/Date、少量循环引用)在 Chrome 128+ 下的平均执行时间排序:
- structuredClone():最快,原生 C++ 实现,无 JS 调用栈开销,约 0.08ms/次
- fast-copy:专为性能设计,智能缓存 + 类型分发,约 0.15ms/次,兼容性优于 structuredClone
- lodash.cloneDeep():功能全但路径长,类型判断多、函数调用频繁,约 0.4–0.6ms/次
- 手写递归(带 WeakMap 缓存):可控但易写低效,约 0.7–1.2ms/次(未优化缓存或类型分支时可达 3ms+)
- JSON.parse(JSON.stringify()):看似快,但序列化成本高,且一遇到 Date/RegExp/undefined 就失真,实测约 0.9ms/次(仅限纯数据)
怎么科学压测深拷贝性能
不能只跑一次 console.time(),要模拟真实负载并排除干扰:
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- 用
performance.now()替代console.time(),精度达微秒级 - 单次测试至少运行 1000 次取平均值,避免 GC 或 JIT 预热偏差
- 构造三类典型数据集分别压测:
• 纯 JSON 数据(验证序列化法上限)
• 含 Map/Set/Date/正则的混合结构(暴露类型处理瓶颈)
• 带自引用或交叉引用的树状结构(考验缓存机制) - 禁用开发者工具(DevTools 打开时 V8 会降频优化),在干净页面下运行
- 示例片段:
function benchmark(cloneFn, data, times = 1000) {
const start = performance.now();
for (let i = 0; i 高频性能瓶颈与对应优化点
多数慢不是因为“深拷贝本身难”,而是实现绕了弯路:
-
重复类型判断:每次递归都用
Object.prototype.toString.call()或instanceof—— 改用预计算 tag 字符串或构造函数映射表 -
缓存失效:用普通 Object 当缓存键 → 键是对象引用,查不到;必须用
WeakMap(支持对象作键)或Map+id标识 -
原型链忽略:拷贝后丢失
constructor或__proto__→ 导致后续instanceof失败,引发隐性重计算 -
过度克隆:对不可变值(如字符串、数字、冻结对象)也走完整流程 → 提前加
Object.isFrozen()或基础类型快路径
工程中真正有效的优化策略
不靠堆代码,而靠“少做+做对”:
- 优先用
structuredClone():Chrome/Firefox/Edge/Safari 16.4+ 已全支持,Node.js 18.13+ 可用,老环境 fallback 到 fast-copy - 对确定结构的数据,改用“按需浅拷贝 + 局部深拷贝”:比如只修改
user.profile.avatar,就只深拷贝profile子树,而非整个 user 对象 - 服务端返回数据时,加
immutable: true标识,前端跳过拷贝直接使用(配合 TypeScript readonly 断言) - 状态管理中,用 Immer 这类库代理变更,内部自动 diff + patch,避免全量深拷贝










