网站首页 > 技术文章 正文
在JavaScript开发中,深拷贝是每个开发者都会遇到的需求。然而,许多开发者习惯使用的JSON.parse(JSON.stringify(obj))方法,却隐藏着诸多陷阱。今天,我们将深入探讨这种方法的缺陷,并介绍一种更可靠、更强大的替代方案——structuredClone。如果你还在为深拷贝问题烦恼,这篇文章绝对值得一读!
JSON.stringify的致命缺陷
JSON.parse(JSON.stringify(obj))虽然看起来简洁,但实际上却是一个充满问题的解决方案。以下是它无法正确处理的几种常见情况:
1.无法处理循环引用
const obj = { name: "对象" };
obj.self = obj; // 循环引用
// 抛出错误: TypeError: Converting circular structure to JSON
const copy = JSON.parse(JSON.stringify(obj));一旦对象中存在循环引用,这种方法就会直接抛出错误,导致程序崩溃。
2.丢失特殊数据类型
JSON.stringify无法处理函数、Symbol、undefined,会将Date转为字符串,RegExp变为空对象,NaN和Infinity变为null。这意味着,如果你的对象中包含这些类型的数据,使用JSON.parse(JSON.stringify(obj))进行深拷贝时,这些数据将丢失或被错误处理。
3.原型链丢失
深拷贝后的对象变成了普通对象,失去了原型链上的所有方法和属性。这对于依赖原型链的代码来说,是一个巨大的问题。
4.Map、Set、WeakMap、WeakSet的处理
这些集合类型在JSON.stringify过程中都变成了空对象,数据完全丢失。这对于需要处理复杂数据结构的开发者来说,是无法接受的。
更可靠的深拷贝替代方案
既然JSON.stringify有这么多问题,那么有没有更好的替代方案呢?答案是肯定的。现代浏览器提供了原生的structuredClone API,它可以处理大多数情况,而且使用起来非常简单:
// 一行代码实现深拷贝
const copy = structuredClone(original);优点
- 支持循环引用:structuredClone可以正确处理循环引用,不会导致程序崩溃。
 - 支持大多数内置类型:它支持Date、RegExp、Map、Set等内置类型,能够保留这些类型的原始结构。
 - 性能优于JSON方法:在性能方面,structuredClone也优于JSON.parse(JSON.stringify(obj))。
 
缺点
- 不支持函数、DOM节点、原型链:虽然structuredClone已经非常强大,但它仍然有一些限制。例如,它不支持函数、DOM节点和原型链。
 
实际案例对比
假设我们有一个包含多种数据类型的复杂对象:
const original = {
  name: "Kimi",
  age: 25,
  date: new Date(),
  regex: /kimi/g,
  map: new Map([["key1", "value1"]]),
  set: new Set([1, 2, 3]),
  symbol: Symbol("kimi"),
  undefinedValue: undefined,
  nanValue: NaN,
  infinityValue: Infinity,
  self: null
};
original.self = original; // 添加循环引用使用JSON.parse(JSON.stringify(original))进行深拷贝时,会遇到以下问题:
- 循环引用导致错误。
 - Date、RegExp、Map、Set等类型的数据丢失或被错误处理。
 - Symbol、undefined、NaN、Infinity等特殊值丢失或被错误处理。
 
而使用structuredClone(original)进行深拷贝时,这些问题都能得到解决:
const copy = structuredClone(original);
console.log(copy); // 正确处理了所有数据类型,包括循环引用为什么你应该选择structuredClone?
- 可靠性:structuredClone能够正确处理循环引用和多种内置类型,避免了JSON.stringify带来的诸多问题。
 - 性能:在性能测试中,structuredClone通常优于JSON.parse(JSON.stringify(obj))。
 - 易用性:只需要一行代码,就能实现深拷贝,简单易用。
 
JSON.parse(JSON.stringify(obj))虽然简洁,但存在太多缺陷,不适合在实际项目中使用。相比之下,structuredClone作为现代浏览器提供的原生API,不仅能够处理循环引用和多种内置类型,而且性能更优。
猜你喜欢
- 2025-09-03 网页数据如何获取,带你走近JS逆向,完全入门级!
 - 2025-09-03 【JavaScript】手撕前端面试题:手写new操作符
 - 2025-06-10 常见web安全问题,SQL注入、XSS、CSRF,基本原理以及如何防御
 - 2025-06-10 web前端之null和undefined区别(前端null与undefined区别)
 - 2025-06-10 Why does Google prepend while(1); to their JSON responses?
 - 2025-06-10 Map与Set:JavaScript的数据魔术师,让你的代码性能原地起飞!
 - 2025-06-10 JavaScript 数据类型详解:从基础到进阶,一篇全搞定!
 - 2025-06-10 JS对象判空的几种方式,你真的会了吗?
 - 2024-07-30 JavaScript面向对象核心知识归纳(javascript面向对象编程)
 - 2024-07-30 前端教程:Javascript Boolean对象
 
- 最近发表
 - 
- 聊一下 gRPC 的 C++ 异步编程_grpc 异步流模式
 - [原创首发]安全日志管理中心实战(3)——开源NIDS之suricata部署
 - 超详细手把手搭建在ubuntu系统的FFmpeg环境
 - Nginx运维之路(Docker多段构建新版本并增加第三方模
 - 92.1K小星星,一款开源免费的远程桌面,让你告别付费远程控制!
 - Go 人脸识别教程_piwigo人脸识别
 - 安卓手机安装Termux——搭建移动服务器
 - ubuntu 安装开发环境(c/c++ 15)_ubuntu安装c++编译器
 - Rust开发环境搭建指南:从安装到镜像配置的零坑实践
 - Windows系统安装VirtualBox构造本地Linux开发环境
 
 
- 标签列表
 - 
- cmd/c (90)
 - c++中::是什么意思 (84)
 - 标签用于 (71)
 - 主键只能有一个吗 (77)
 - c#console.writeline不显示 (95)
 - pythoncase语句 (88)
 - es6includes (74)
 - sqlset (76)
 - apt-getinstall-y (100)
 - node_modules怎么生成 (87)
 - chromepost (71)
 - flexdirection (73)
 - c++int转char (80)
 - mysqlany_value (79)
 - static函数和普通函数 (84)
 - el-date-picker开始日期早于结束日期 (76)
 - js判断是否是json字符串 (75)
 - c语言min函数头文件 (77)
 - asynccallback (87)
 - localstorage.removeitem (77)
 - vector线程安全吗 (73)
 - java (73)
 - js数组插入 (83)
 - mac安装java (72)
 - 无效的列索引 (74)
 
 
