JavaScript与C语言参数传递方式的比较

在阅读《高程》的时候,里面提到JS中所有函数的参数都是按值传递的,但是感觉引用类型做参数的时候,跟C中的按值传递并不完全一样,倒跟C语言中的指针传递很相似。

这里我们先跟C语言中的参数传递做一下对比。

C/C++中的参数传递

C/C++中参数传递共分为三种:

值传递

只是把实参的值传给形参,形参和实参之间并没有关联。

指针传递/地址传递

将变量的地址作为实参传递给形参,其实指针传递本质上来讲是一种特殊的值传递,传递的值是变量的地址。

1
2
3
4
void foo(int *x);//函数定义

int a;
foo(&a);//函数调用
引用传递

形参对实参进行引用,也就是说形参只是实参的一个别名。

1
2
3
4
void foo(int &x);//函数定义

int a;
foo(a);//函数调用

JavaScript中的参数传递

JavaScript中的引用类型变量存储的是一个内存块的地址,它作为实参传递的过程中会把这个地址直接赋给形参,这个传递的过程是值传递的。由于跟基本类型变量的传递有所不同,也可以叫做共享传递。

所以《高程》中说:JavaScript中所有函数的参数都是按值传递。

这与C语言中的指针传递基本类似,两者传递的都是地址的副本。主要的区别在于JavaScript不能像C一样直接获取或者操作变量的地址,引用类型变量存储的就是自身的地址,只不过这个地址并不对开发者开放,通过地址来获取地址所指向数据的过程由引擎完成。体会一下下面两段代码的区别:

C语言中的指针传递/地址传递:

1
2
3
4
5
6
7
void foo(int *x)
{
*x = b;
}

int a;
foo(&a);

JavaScript中的共享传递:

1
2
3
4
5
6
7
var foo(x)
{
x = b;
}

var a = new Object();
foo(a);

这样看来,下面的代码就很好理解了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obj = {x : 1};
function foo(o) {
o.x = 2;//并没有修改o中的地址,通过这个地址所做的改动,都会影响实参的值
}
foo(obj);
console.log(obj.x); // 2


var obj = {x : 1};
function foo(o) {
o = {x : 2};//o中的地址已经已经不是实参obj的地址,此后o的任何改动都与实参obj无关
}
foo(obj);
console.log(obj.x); // 1

上述操作,类比到C语言中的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int a = 1;
function foo(int *x) {
*x = 2;//并没有修改x中的地址,通过这个地址所做的改动,都会影响实参的值
}
foo(&a);
console.log(a); // 2


int a = 1;
int b = 2;
function foo(int *x) {
x = &b;//将x中的地址改为变量b的地址,此后x的任何改动都与实参a无关
}
foo(&a);
console.log(a); // 2