JavaScript 函数传参机制:为什么是“按共享传递”而非按引用?
许多开发者认为 JavaScript 中对象是按引用传递,原始值是传值,但这种理解并不完全准确。本文将详细拆解 JavaScript 的求值策略,揭示其实际采用的"按共享传递"(Call by sharing)机制,解释为何修改对象属性会影响外部变量,但给参数重新赋值却不会改变外部引用。
什么是求值策略?
在计算机科学中,求值策略(Evaluation strategy)决定了编程语言如何计算表达式中的参数并将其传递给函数。常见的分类包括:
- 严格模式:参数在函数调用前立即计算。
- 非严格模式:延迟计算(如惰性求值)。
在 ECMAScript(JavaScript 的核心标准)中,参数采用的是严格的传参策略,即传入的参数在进入函数体之前就已经被计算完毕。
三种核心传参策略对比
1. 按值传递 (Call by Value)
这是最直观的策略。当函数接收参数时,会创建一个参数值的完整副本。函数内部对参数的任何修改都不会影响外部的原始值。
// 伪代码演示
bar = 10
procedure foo(barArg):
barArg = 20;
end
foo(bar)
print(bar) // 输出: 10 (外部不受影响)
这种方式在处理简单数值类型时效率很高,但对于大型结构体,频繁的内存拷贝会导致性能问题。
2. 按引用传递 (Call by Reference)
在这种策略下,函数接收到的是对象的直接地址引用。参数成为外部变量的别名。无论修改属性还是重新赋值,都会直接影响外部变量。
// 伪代码演示
bar = { x: 10, y: 20 }
procedure foo(barArg):
barArg.x = 100 // 修改属性
barArg = { z: 1 } // 重新赋值
end
foo(bar)
print(bar) // 输出: { z: 1 } (外部彻底改变)
这种策略能高效处理大对象,但因为赋予了函数极高的控制权,容易引发意外副作用。
3. 按共享传递 (Call by Sharing) - JavaScript 的真实机制
大多数现代语言(Java、Python、Ruby、JavaScript)实际上使用的策略被称为“按共享传递”,也称为“按对象传递”。这一概念由 Barbara Liskov 于 1974 年提出。
核心规则:
函数接收的是对象引用的副本(Copy of the reference),而非对象本身的副本,也不是原始引用的别名。
- 修改属性(Mutation):因为副本和原始引用指向堆内存中的同一个对象,所以函数内修改对象属性会反映到外部。
- 重新赋值(Reassignment):函数内的参数只是一个局部变量名。给它赋新值只是让它指向了另一个地址,这不会影响外部的引用。
让我们通过 JavaScript 的具体行为来验证这一点:
var obj = { name: 'Initial' };
function modify(arg) {
// 修改属性:影响外部
arg.name = 'Modified';
// 重新赋值:不影响外部
arg = { name: 'New Object' };
}
modify(obj);
console.log(obj.name); // 输出: "Modified"
从内存角度看:
1. obj 绑定到内存地址 A。
2. 调用函数时,arg 也被赋予指向地址 A 的权限(它是副本,但指向相同目的地)。
3. arg.name = 'Modified' 操作的是地址 A 的内容,所以 obj 看到的是变化。
4. arg = ... 让局部变量 arg 转向新的地址 B,但这切断了它与地址 A 的联系,旧的对象(以及外部的 obj)依然稳稳地待在地址 A。
常见的术语误区
“JavaScript 对象是按引用传递的吗?”
这是一个广为流传的错误说法。如果你执行以下代码:
var a = {x: 1};
var b = a; // 此时 a 和 b 共享同一对象
b.x = 2;
console.log(a.x); // 2
b = {y: 3}; // b 换了一个对象
console.log(a.x); // 2 (a 没变!如果是按引用传递,这里逻辑会更复杂或预期不同,但实际上是因为 b 不再是引用而是新绑定)
Brendan Eich(JS 发明者)明确指出:JavaScript 传递的是引用的副本。如果按严格的学术定义,这属于“按共享传递”。但在日常开发交流中,为了简化,人们有时仍笼统地称之为“按值传递”——只不过这里的“值”是一个特殊的“指针值”。
结论
掌握这一概念对于编写健壮的前端代码至关重要。当你试图在函数内部通过重新赋值来重置一个对象时,请记住:你必须返回新对象并重新赋值给外部变量,仅仅改变形参是无效的。
以上就是 JavaScript 函数参数传值机制的详细内容,更多关于 JavaScript 求值策略的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













