JavaScript引用语义指变量存储对象地址而非数据本身;浅拷贝仅复制顶层引用,嵌套对象仍共享内存;深拷贝则递归创建新堆空间,彻底隔离对象。

理解 JavaScript 堆内存中的“引用语义”,关键在于抓住一点:变量不直接存对象,而是存一个指向堆中真实数据的地址。
堆内存里存的是对象本体,栈里只存“门牌号”
当你写 let obj = { name: "张三", info: { age: 25 } },JS 引擎会做两件事:
- 在堆内存中开辟一块空间,真正存放
{ name: "张三", info: { age: 25 } }这个结构; - 在栈内存中给
obj存一个“地址”(比如0x1a2b),它就像一张门牌号,指向堆里的那块数据。
这个“地址”就是所谓的引用——引用语义的本质,就是操作地址,而非数据本身。
浅拷贝只是复制了“门牌号”,没复制“房子”
像 const copy = { ...obj } 或 Object.assign({}, obj) 这类操作,只做了这一件事:在栈里新建一个变量,把 obj 的门牌号(如 0x1a2b)原样抄一份给 copy。
-
obj.name和copy.name是基本类型,值被单独复制,互不影响; -
obj.info和copy.info都是对象,它们各自在栈中存的仍是同一个地址(比如0x3c4d),指向堆中同一份{ age: 25 }。
所以改 copy.info.age = 26,等于拿着 0x3c4d 这个门牌号进屋改东西——obj.info.age 自然也变成 26。
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
嵌套越深,共享越隐蔽
浅拷贝只处理第一层属性。如果对象是三层嵌套:{ a: { b: { c: 1 } } },那么:
- 顶层
a的引用被复制; - 第二层
b的引用也被复制(但仍是同一地址); - 第三层
c的值虽是基本类型,可它的容器b没变,所以修改copy.a.b.c仍会同步到原对象。
这不是方法“偷懒”,而是设计使然:浅拷贝默认信任你只需要隔离表层,节省性能和内存。
要真正隔离,就得在堆里再造一套“房子”
深拷贝(如 structuredClone(obj) 或 _.cloneDeep(obj))会递归遍历,对每个嵌套对象都执行“分配新堆空间 + 复制内容”的动作:
- 为
obj.info在堆中新开一块内存(比如0x5e6f),再把{ age: 25 }完整搬进去; - 让
copy.info指向这个新地址,而不是原来的0x3c4d。
至此,两个对象才彻底解耦——改任何一个,都不会牵动另一个。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










