JavaScript函数参数均为值传递,即基本类型传值副本、对象传地址副本;修改对象属性会影响外部,但重赋值不会;应通过浅拷贝或返回新对象避免意外修改。
JavaScript 中所有函数参数都是值传递,没有真正的引用传递。 这句话不是绕口令,而是理解 JS 参数行为的关键前提。很多人看到 obj.name = 'Jerry' 能改外部对象,就以为是“引用传递”,其实只是传了一个地址的副本——它既不是纯值,也不是真引用,而是一种“传引用的副本”(pass by sharing)。
因为数字、字符串、布尔值等原始类型存储在栈中,传参时直接复制值本身:
function changeNum(n) {
n = 100;
}
let x = 42;
changeNum(x);
console.log(x); // 42,没变
n 是 x 的独立副本,改 n 就像改一张照片的打印件,原底片不受影响number、string、boolean、null、undefined、symbol 上完全一致因为传的是堆内存中对象地址的副本,形参和实参指向同一块内存区域:
function mutateObj(o) {
o.age = 30; // ✅ 修改属性:生效
o = { name: 'Bob' }; // ❌ 重赋值:断开连接,不影响外部
}
let person = { name: 'Alice' };
mutateObj(person);
console.log(person.age); // 30
console.log(person.name); // 'Alice'(没变成 'Bob')
o.age = 30 是通过地址找到原对象并写入,所以外部可见o = {...} 是让局部变量 o 指向新对象,原 person 变量仍指着旧地址实际开发中,最容易栽
跟头的地方不是“能不能改”,而是“什么时候意外改了”:
立即学习“Java免费学习笔记(深入)”;
Array.prototype.push()、.pop()、.splice() 等方法操作传入的数组,会直接污染原始数组——除非你明确想这样,否则应先 [...arr] 或 arr.slice() 浅拷贝{ name } = obj 拿出属性后修改,不会影响 obj;但若解构后又执行 obj.name = 'xxx',就又回到共享地址的老问题return { ...obj, age: newAge })比原地修改更安全,尤其在 React/Vue 的响应式逻辑中,避免触发不必要的更新=== 判断两个变量是否指向同一对象,比“看起来一样”更可靠真正容易被忽略的,不是“对象能被改”,而是“改的是谁的副本”。只要记住:JS 里没有魔法,只有栈里的地址拷贝 + 堆里的真实数据。传什么,就只能动什么;想不动原数据,就得自己做拷贝。