按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。
按引用传递(call by reference)时,函数的形参接收实参的隐式引用,而不再是副本。这意味着函数形参的值如果被修改,实参也会被修改。同时两者指向相同的值。
按引用传递会使函数调用的追踪更加困难,有时也会引起一些微妙的BUG。
按值传递由于每次都需要克隆副本,对一些复杂类型,性能较低。两种传值方式都有各自的问题。
我们先看一个C的例子来了解按值和引用传递的区别:
复制代码 代码如下: void Modify(int p, int * q) { p = 27; // 按值传递 - p是实参a的副本, 只有p被修改 *q = 27; // q是b的引用,q和b都被修改 } int main() { int a = 1; int b = 1; Modify(a, &b); // a 按值传递, b 按引用传递, // a 未变化, b 改变了 return(0); }
这里我们可以看到:
a => p按值传递时,修改形参p的值并不影响实参a,p只是a的副本。 b => q是按引用传递,修改形参q的值时也影响到了实参b的值。 探究JS值的传递方式 JS的基本类型,是按值传递的。 复制代码 代码如下: var a = 1; function foo(x) { x = 2; } foo(a); console.log(a); // 仍为1, 未受x = 2赋值所影响
再来看对象:
复制代码 代码如下: var obj = {x : 1}; function foo(o) { o.x = 3; } foo(obj); console.log(obj.x); // 3, 被修改了!
说明o和obj是同一个对象,o不是obj的副本。所以不是按值传递。 但这样是否说明JS的对象是按引用传递的呢?我们再看下面的例子: 复制代码 代码如下: var obj = {x : 1}; function foo(o) { o = 100; } foo(obj); console.log(obj.x); // 仍然是1, obj并未被修改为100.
按共享传递 call by sharing 准确的说,JS中的基本类型按值传递,对象类型按共享传递的(call by sharing,也叫按对象传递、按对象共享传递)。最早由Barbara Liskov. 在1974年的GLU语言中提出。该求值策略被用于Python、Java、Ruby、JS等多种语言。
该策略的重点是:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)。 它和按引用传递的不同在于:在共享传递中对函数形参的赋值,不会影响实参的值。如下面例子中,不可以通过修改形参o的值,来修改obj的值。 复制代码 代码如下: var obj = {x : 1}; function foo(o) { o = 100; } foo(obj); console.log(obj.x); // 仍然是1, obj并未被修改为100.