JavaScript中对象传参是传引用值的副本,修改属性会影响外部对象,但重新赋值形参不影响实参;需用浅拷贝或深拷贝隔离修改。

JavaScript 中对象作为参数传递时,函数内部对对象属性的修改会影响外部实参,这并非“引用传递”,而是传的是对象引用的副本(即地址值的拷贝),该副本仍指向堆内存中的同一对象。理解这一点,是掌握行为本质的关键。
堆与栈的分工:对象存储的真实位置
基本类型(如 number、string、boolean)值直接存在栈中;而对象(包括数组、函数、日期等)本身存在堆内存中,变量在栈中只保存一个指向堆中对象的地址(类似门牌号)。当把对象传入函数时,实际上传递的是这个地址值的一份拷贝——不是对象本身,也不是“引用的引用”,就是一个普通的数值型地址副本。
- 栈中变量 a 存的是 0x1234(假设为某对象地址)
- 调用 fn(a) 时,形参 obj 在栈中新分配空间,也存入 0x1234
- obj 和 a 指向堆中同一个对象,因此修改 obj.prop 就等于修改那个对象的属性
为什么重新赋值形参不影响实参?
如果在函数内执行 obj = { name: 'new' },只是让形参 obj 的栈空间改存另一个新地址(比如 0x5678),而原始变量 a 的栈空间仍为 0x1234,未被改动。此时 obj 和 a 已指向不同对象,后续操作互不干扰。
- ✅ 修改属性:obj.name = 'Jack' → 影响 a.name(因共用同一堆对象)
- ❌ 替换引用:obj = { name: 'Jack' } → 不影响 a(a 仍指向原对象)
如何真正隔离修改?浅拷贝与深拷贝的边界
若需避免副作用,必须创建新对象,使形参指向独立内存。但要注意拷贝层级:
- 浅拷贝(如 Object.assign({}, obj)、{...obj}、Array.from(arr)):仅复制第一层属性,嵌套对象仍共享引用
- 深拷贝(如 structuredClone(obj)、JSON.parse(JSON.stringify(obj))(有局限)):递归复制所有层级,彻底隔离
- 函数参数设计时,可约定“不修改入参”,或文档注明是否会产生副作用
对比其他语言有助于厘清概念
Java 同样是“传值”,只不过传的是引用类型的地址值;Python 所谓“传对象引用”本质上也是传对象 ID 的副本。JS 并无真正的“引用传递”(如 C++ & 或 Java 的某些反射机制),ECMAScript 规范明确所有参数都是按值传递(call by value)。
- 所谓“引用传递”是常见误称,准确说法是:对象类型传的是引用值的副本
- 区分“值类型”和“引用类型”的意义在于内存布局和传递效果,而非语法层面的传递机制分类

















