当前位置: 首页 > news >正文

前端必备技能:彻底搞懂JavaScript深浅拷贝,告别数据共享的坑!

❤ 写在前面
如果觉得对你有帮助的话,点个小❤❤ 吧,你的支持是对我最大的鼓励~
个人独立开发wx小程序,感谢支持!


开篇故事:为什么我的数据“打架”了?

想象一下这个场景:你在开发一个购物车功能,复制了一个商品对象准备修改数量,结果发现原始商品的数据也变了!这种“灵异事件”让很多前端开发者头疼不已。

letoriginalProduct={name:"JavaScript高级程序设计",price:99,details:{publisher:"人民邮电出版社",pages:728}};// 你以为的“复制”letcopiedProduct=originalProduct;copiedProduct.price=79;// 修改副本的价格console.log(originalProduct.price);// 79?!原对象也被修改了!

这就是我们今天要解决的“深浅拷贝”问题!下面,让我们一步步解开这个谜团。

内存模型:理解深浅拷贝的基础

在深入之前,我们先看看JavaScript中数据是如何存储的:

┌─────────────┐ ┌───────────────┐ │ 栈内存 │ │ 堆内存 │ │ (Stack) │ │ (Heap) │ ├─────────────┤ ├───────────────┤ │ 基本类型 │ │ │ │ 变量名|值 │ │ 引用类型 │ │ a -> 10 │ │ 的对象数据 │ │ b -> true │ │ {name: "xxx"}│ │ │ │ [1,2,3] │ │ obj1 -> ↗═══╪══════> {x: 1, y: 2} │ │ obj2 -> ↗═══╪══════> {name: "test"}│ └─────────────┘ └───────────────┘

基本类型(Number, String, Boolean等)直接存储在栈内存中,而引用类型(Object, Array等)在栈中只存储地址指针,真正的数据在堆内存中。

深浅拷贝对比流程图

原始对象

选择拷贝方式

浅拷贝

深拷贝

只复制第一层属性

嵌套对象仍共享内存

修改嵌套属性会影响原对象

递归复制所有层级

完全独立的新对象

新旧对象互不影响

适用场景:简单数据结构

适用场景:复杂嵌套对象

浅拷贝:只挖第一层

浅拷贝就像只复制房子的钥匙,不复制房子里的家具。

常见的浅拷贝方法

方法1:展开运算符(最常用)
letshallowCopy={...originalObject};letshallowCopyArray=[...originalArray];
方法2:Object.assign()
letshallowCopy=Object.assign({},originalObject);
方法3:数组的slice()和concat()
letshallowCopyArray=originalArray.slice();letanotherCopy=originalArray.concat();

浅拷贝的陷阱

letuser={name:"小明",settings:{theme:"dark",notifications:true}};letuserCopy={...user};userCopy.name="小红";// ✅ 不会影响原对象userCopy.settings.theme="light";// ❌ 原对象的theme也被修改了!console.log(user.settings.theme);// "light" 中招了!

深拷贝:连根拔起的复制

深拷贝是真正的"克隆",创建一个完全独立的新对象。

方法1:JSON大法(最简单但有局限)

letdeepCopy=JSON.parse(JSON.stringify(originalObject));

注意限制:

  • 不能复制函数、undefined、Symbol
  • 不能处理循环引用
  • Date对象会变成字符串

方法2:手写递归深拷贝函数

functiondeepClone(obj,hash=newWeakMap()){// 处理基本类型和nullif(obj===null||typeofobj!=='object'){returnobj;}// 处理Dateif(objinstanceofDate){returnnewDate(obj);}// 处理数组if(Array.isArray(obj)){returnobj.map(item=>deepClone(item,hash));}// 处理普通对象if(hash.has(obj)){returnhash.get(obj);// 解决循环引用}letcloneObj=Object.create(Object.getPrototypeOf(obj));hash.set(obj,cloneObj);for(letkeyinobj){if(obj.hasOwnProperty(key)){cloneObj[key]=deepClone(obj[key],hash);}}returncloneObj;}// 测试letcomplexObj={name:"测试",date:newDate(),nested:{x:1},arr:[1,2,[3,4]]};complexObj.self=complexObj;// 循环引用letcloned=deepClone(complexObj);console.log(cloned!==complexObj);// trueconsole.log(cloned.nested!==complexObj.nested);// trueconsole.log(cloned.self===cloned);// true,循环引用正确处理

方法3:使用现成库

// lodashimport_from'lodash';letdeepCopy=_.cloneDeep(originalObject);// 或者使用structuredClone(现代浏览器原生API)if(typeofstructuredClone==='function'){letdeepCopy=structuredClone(originalObject);}

实战场景:什么时候用什么拷贝?

场景1:表单编辑(推荐深拷贝)

// 编辑前深拷贝原始数据leteditData=deepClone(originalData);// 用户随意编辑...editData.user.profile.avatar="new_avatar.jpg";// 点击取消,原始数据完好无损// 点击保存,提交editData

场景2:状态管理中的状态更新(浅拷贝足够)

// Redux reducer中的状态更新functionreducer(state=initialState,action){switch(action.type){case'UPDATE_SETTINGS':return{...state,// 浅拷贝settings:{...state.settings,// 嵌套对象也需要展开theme:action.payload}};// ...}}

场景3:配置对象合并(浅拷贝+深度合并)

functionmergeConfig(defaultConfig,userConfig){return{...defaultConfig,...userConfig,// 深度合并嵌套对象options:{...defaultConfig.options,...userConfig.options}};}

性能考虑:深浅拷贝的选择

方法速度内存占用适用场景
浅拷贝⚡⚡⚡⚡⚡(快)简单对象,无嵌套修改
JSON深拷贝⚡⚡⚡(中)数据简单,无函数/日期
递归深拷贝⚡⚡(慢)复杂对象,需要完整复制
structuredClone⚡⚡⚡⚡(较快)现代浏览器,需要原生支持

总结:拷贝选择速查表

  1. 只需要复制第一层数据?→ 使用展开运算符...
  2. 需要完全独立的副本?→ 使用深拷贝
  3. 数据简单,不含函数/日期?JSON.parse(JSON.stringify())
  4. 需要处理复杂类型和循环引用?→ 手写递归或使用lodash
  5. 现代浏览器环境?→ 尝试structuredClone

记住这个黄金法则:当你不知道嵌套属性是否需要独立修改时,选择深拷贝更安全!

互动挑战

试试看你能看出下面代码的输出吗?

letpuzzle={a:1,b:{inner:2},c:[3,4]};letcopy1={...puzzle};letcopy2=JSON.parse(JSON.stringify(puzzle));copy1.b.inner=999;copy1.c.push(888);console.log(puzzle.b.inner);// 是多少?console.log(puzzle.c.length);// 是多少?console.log(copy2.b.inner);// 是多少?

答案:第一个是999(浅拷贝共享嵌套对象),第二个是4(数组也被修改了),第三个是2(深拷贝完全独立)。

希望这篇博客能帮你彻底理解JavaScript中的深浅拷贝!下次遇到数据"打架"的情况,你就知道该怎么处理了。

http://www.jsqmd.com/news/159313/

相关文章:

  • 2025年知名的CQC数据中心机房值得信赖榜 - 品牌宣传支持者
  • PyTorch-CUDA-v2.7镜像能否支持Triton推理服务器
  • 2025常州AI搜索优化服务商五强解析:企业如何抢占生成式搜索心智高地 - 2025年品牌推荐榜
  • MCP协议深度解析:提升AI应用能力的关键,附大模型学习资料(必收藏)
  • PyTorch-CUDA-v2.7镜像资源占用情况测试报告
  • PyTorch-CUDA-v2.7镜像支持Windows/Linux/Mac吗?
  • 2026年全球雇佣趋势:Safeguard Global在马来西亚提供名义雇主EOR服务的核心价值 - 品牌2025
  • 数字集成电路设计核心考点与 Verilog 实战指南
  • 昨天还涨停,今天就被“闷杀”?深挖A股背后看不见的“收割机”
  • PyTorch-CUDA镜像如何导出训练好的模型文件
  • 2026北京市石景山区财产分割律师值得信赖的TOP5实力榜律师事务所,案例丰富财产分割律师推荐 - 苏木2025
  • 【Java毕设全套源码+文档】基于Java兰州市出租车服务管理系统的设计与实现(丰富项目+远程调试+讲解+定制)
  • PyTorch-CUDA镜像在搜索引擎排序优化中的作用
  • VMware Debian 挂载 Windows 文件夹至 Debian 目录
  • PyTorch-CUDA-v2.7镜像对时间序列预测的帮助
  • 2025年终端应用全链路性能测试实施策略与实践对比
  • PyTorch-CUDA环境运行BERT模型的性能实测
  • 深耕GEO贴牌代理,解锁AI搜索优化精准触达客户新路径 - 源码云科技
  • 2025年诚信的日本移民机构推荐,服务不错的日本移民企业全解析 - 工业推荐榜
  • 2025年AI智能体(Agent)开发平台全攻略:从零基础到专业开发的最佳选择(收藏必看)
  • 2025年主流测试用例管理平台对比分析与最佳实践
  • PyTorch-CUDA-v2.7镜像在云服务器上的最佳实践
  • 2025年质量好的水处理剂聚合硫酸铁/工业级聚合硫酸铁厂家最新热销排行 - 品牌宣传支持者
  • 建议收藏】MCP协议从入门到实践:基于Dify的大模型工具调用全解析
  • 深圳量化私募紧急寻找互联网大厂的C++,学历好,1-5年经验之间的,最好是2-3年经验的,看深圳机会的小伙伴们快来,预算足,制度好,福利多#C++ #量化开发工程师#互联网工程师#开发工程师#C+
  • 我发现联邦学习加自监督学习破解跨境罕见病早筛数据孤岛
  • PyTorch-CUDA镜像在Kubernetes集群中的部署案例
  • 2025苏州包装材料公司TOP5权威推荐:浩鑫包装性价比好不好? - myqiye
  • PyTorch-CUDA-v2.7镜像是否支持A100/H100?官方回应来了
  • PyTorch-CUDA-v2.7镜像更新日志:修复多个已知问题