JavaScript 参数传递机制详解:按值传递与按共享传递

作者:木棉时间:2026-10-24 09:57:48浏览:600

本文深入探讨 JavaScript 的函数参数传递机制。虽然 ECMAScript 规定所有参数均为按值传递,但针对引用类型,实际表现为按共享传递(Call by Sharing)。文章通过基本类型与对象类型的代码示例,解析变量复制、属性修改与重新赋值的区别,并补充 ES6 默认参数的惰性求值特性。

近期在研究 lambda 演算中的 η-变换在 JavaScript 中的应用时,偶然发现 StackOverflow 上一个关于 JavaScript 求值策略的有趣讨论:函数参数究竟是按值传递还是按引用传递?该问题的回答颇具代表性。

核心结论概述


function changeStuff(a, b, c) {
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);         // 10
console.log(obj1.item);   // changed
console.log(obj2.item);   // unchanged

若假设 JavaScript 函数参数采用按值传递,则在函数 changeStuff 内部修改 b.item 的值时,不应影响外部 obj1 对象的值。

若假设 JavaScript 函数参数采用按引用传递,则函数 changeStuff 内部的修改应影响外部所有变量定义,例如 num 变为 100、obj2.item 变为 changed。然而实际情况并非如此。

因此,不能简单断言 JavaScript 参数传递严格属于按值传递或按引用传递。总体而言,JavaScript 函数的参数传递机制可概括为按值传递。此外,JavaScript 还采用了一种称为按共享传递(Call by Sharing)的策略,具体表现取决于参数的类型。

当参数为基本类型时,遵循按值传递规则;

当参数为引用类型时,遵循按共享传递规则。

参数传递机制解析

根据《JavaScript 高级程序设计》所述,ECMAScript 中所有函数参数均按值传递。这意味着将函数外部的值复制到函数内部的参数中,其过程类似于将值从一个变量复制到另一个变量。基本类型值的传递等同于基本类型变量的复制,而引用类型值的传递则等同于引用类型变量的复制。

红宝书指出所有函数参数均按值传递,这是否准确?让我们结合上述示例进行分析:

按值传递机制

在 JavaScript 中,基本类型作为参数时采用按值传递(Call by Value)策略:


function foo(a) {
  a = a * 10;
}

var num = 10;

foo(num);

console.log(num); // 10 没有变化

从代码可见,函数内部对参数的修改并未影响外部变量,证实了按值传递的特性。

按共享传递机制

在 JavaScript 中,对象作为参数时采用按共享传递(Call by Sharing)策略:

修改参数的属性会反映到外部对象上。

对参数进行重新赋值则不会影响外部对象。

根据上述示例,函数内部修改了参数 b 的属性 item,导致外部对象 obj1 的属性 item 也随之改变。


function bar(b) {
  b.item = "changed";
  console.log(b === obj1) // true
}

var obj1 = {item: "unchanged"};

bar(obj1);

console.log(obj1.item);   // changed 修改参数的属性将会影响到外部对象

从 b === obj1 打印结果为 true 可以看出,函数内部修改参数属性并未改变参数本身的引用。b 和 obj1 共享同一个对象地址,因此修改参数属性会影响外部对象。

然而,若将参数 c 重新赋值为一个新对象,则不会影响外部对象。


function baz(c) {
  c = {item: "changed"};
  console.log(c === obj2) // false
}

var obj2 = {item: "unchanged"};

baz(obj2);

console.log(obj2.item);   // unchanged 重新赋值将不会影响到外部对象

当参数 c 被重新赋值为一个新对象时,c 将绑定到新的对象地址,此时 c === obj2 打印结果为 false,表明它们不再共享同一对象地址。由于各自拥有独立的对象地址,因此重新赋值不会影响外部对象。

总结

可以说,按共享传递是按值传递的一种特例,其传递的是引用地址的副本。因此,红宝书的说法也是正确的。

可以将 ECMAScript 函数的参数视为局部变量。-- 《JavaScript 高级程序设计》

延伸:惰性求值

前文已明确所有函数参数均按值传递,且 JavaScript 中参数需先求值再作为实参传入函数。但在 ES6 中存在一个特例。

参数默认值并非传值,而是每次调用时重新计算默认值表达式的结果,即参数默认值采用惰性求值。-- 《ECMAScript 6 入门》


let x = 99;
function foo(p = x + 1) {
  console.log(p);
}

foo() // 100

x = 100;
foo() // 101

在上述代码中,参数 p 的默认值为 x + 1。每次调用函数 foo 时,都会重新计算 x + 1,而非默认 p 等于 100。

以上即为关于 JavaScript 求值策略的详细讨论,更多 JavaScript 求值策略相关资料请关注本站其他文章!

作者最新文章