形参和实参指向堆中同一对象,修改属性会相互影响;赋值新对象则断开联系。主流语言均采用“传地址值的副本”,本质是按值传递。

因为形参和实参指向堆内存中同一个对象,修改属性就是直接操作那块共享的内存。
堆内存里只存一份对象
对象实际存在堆中,变量(包括实参和形参)在栈里只存一个地址——就像两个便签都写着“302房间”,进屋改东西,谁都能看到变化。
例如:
- 实参 a = { name: 'Tom' } → 栈中存地址 0x100,堆中 0x100 处存着 { name: 'Tom' }
- 调用 fn(a) → 形参 obj 的栈空间也写上 0x100
- 执行 obj.name = 'Jack' → 程序去 0x100 那个位置把 name 改了,a.name 自然变成 'Jack'
改的是内容,不是地址
关键区别在于:属性修改不改变地址值,只是更新堆中已有对象的字段;而 obj = {...} 是让形参换了个新地址,和实参再无关系。
常见操作对比:
- obj.count++、obj.items.push(1)、arr[0] = 'x' → 共享对象被改,外部可见
- obj = { x: 1 }、arr = [9] → 形参指向新内存,实参原地不动
所有主流语言逻辑一致
Java、JavaScript、Python 对引用类型参数都采用“传地址值的副本”机制,并非真正引用传递。规范上统一叫“按值传递”,只不过这个“值”对引用类型来说是内存地址。
所以不是语言特性差异,而是内存模型的自然结果:只要两个变量指向同一块堆内存,改内容就必然互相影响。











