JavaScript 参数传递是按值还是按引用?
简化版
JavaScript 参数传递本质上都是按值传递:原始类型传的是值的副本,对象类型传的是“引用值”的副本。函数内修改对象属性会影响外部对象,但把参数重新赋值为新对象不会改变外部变量绑定。
详细版
示例:
function change(obj) {
obj.name = 'Bob';
obj = { name: 'Carol' };
}
const user = { name: 'Alice' };
change(user);
console.log(user.name); // Bob
很多人说“对象按引用传递”,这容易误导。 更准确的说法是:对象变量里保存的是引用值,传参时复制了这个引用值。 因此函数内能通过这份引用找到同一个对象并改属性,但不能通过重新给参数赋值来改变外部变量指向。
完整版教学
一、先区分变量、值和对象
JavaScript 变量不是盒子里直接装对象。 对原始类型,变量保存具体值。 对对象类型,变量保存能找到对象的引用值。
let a = 1;
let b = { count: 1 };
可以简化成:
a -> 1
b -> 引用地址 -> { count: 1 }
这个模型不是引擎真实内存图,但足够解释面试题。 关键是:变量绑定和值是两回事,对象本体又是另一回事。
二、函数传参时复制的是参数值
调用函数时,实参的值会赋给形参。 如果实参是原始值,就复制原始值。 如果实参是对象引用值,就复制这个引用值。
function add(x) {
x = x + 1;
}
let n = 1;
add(n);
console.log(n); // 1
数字例子很好理解:n 的值 1 被复制给 x。
函数里把 x 改成 2,不会影响外部 n。
这是按值传递的基本表现。
三、对象属性修改为什么能影响外部
对象传参时复制的是引用值。 形参和外部变量暂时指向同一个对象。 所以通过形参修改对象属性,会改到同一个对象本体。
function update(user) {
user.age = 20;
}
const u = { age: 18 };
update(u);
console.log(u.age); // 20
流程如下:
u ----\
-> { age: 18 }
user -/
user.age = 20
-> { age: 20 }
这就是很多人误以为“对象按引用传递”的来源。 但只说这半句会解释不了重新赋值的行为。
四、重新赋值参数不会改变外部变量
如果在函数里把形参改成另一个对象,只是让形参指向新对象。 外部变量仍然指向原对象。 这是判断“按值传递”的核心例子。
function reset(user) {
user = { age: 0 };
}
const u = { age: 18 };
reset(u);
console.log(u.age); // 18
| 操作 | 是否影响外部对象 | 原因 |
|---|---|---|
user.age = 20 | 是 | 改同一个对象的属性 |
user = { age: 0 } | 否 | 只改形参绑定 |
u = { age: 0 } | 是 | 改外部变量自己的绑定 |
这个表格能把“改对象”和“改变量指向”分开。
五、数组也是对象,规则完全一样
数组传参没有特殊规则。
push、splice 会修改同一个数组对象。
重新赋值为新数组不会改变外部变量。
function demo(list) {
list.push(3);
list = [9, 9, 9];
}
const arr = [1, 2];
demo(arr);
console.log(arr); // [1, 2, 3]
这个例子里 push 生效,list = [...] 不影响外部。
如果希望函数不产生副作用,可以返回新数组,而不是直接改入参。
六、工程里要关注可变性和副作用
理解参数传递规则后,工程重点是控制副作用。 函数如果会修改传入对象,应在命名、文档或类型上明确表达。 不希望外部被影响时,可以浅拷贝或深拷贝后再操作。
function rename(user, name) {
return { ...user, name };
}
记忆钩子:JS 传参复制的是“格子里的值”;对象变量格子里放的是引用值,所以能改对象,不能改外部变量格子。
React、Redux 等状态管理里强调不可变更新,正是为了避免这种共享对象修改带来的状态难追踪。
七、常见误区与追问
- 误区:对象在 JavaScript 中是按引用传递。 更准确是引用值按值传递,形参拿到的是引用值副本。
- 误区:函数里改对象属性和重赋值参数是一回事。 前者改对象本体,后者改形参绑定。
- 误区:数组有不同的传参规则。 数组也是对象,和普通对象遵循同一套规则。
- 追问:为什么
push会影响外部数组? 形参和外部变量引用同一个数组对象,push修改的是对象本体。 - 追问:如何避免函数修改外部对象? 使用拷贝、不可变更新或冻结对象,并约定函数返回新值。
- 追问:浅拷贝能完全避免影响吗? 不能,浅拷贝只复制第一层,嵌套对象仍可能共享引用。
八、加强记忆
这题的稳定答法是:JavaScript 只有按值传递;对象变量的值恰好是引用。先用原始类型说明形参重赋值不影响外部,再用对象属性修改和对象重赋值对比,最后补数组同理和副作用控制。