JavaScript中值传递和引用传递的典例探究

目录

概述

  在Javascript中,基本类型(null, undefined, number, string, boolean, symbol)是通过值传递方式进行,而复合类型(object, array, function)则是通过引用传递方式进行。
  但是,由于Javascript工作机制所决定,在js中,引用指向的其实是值,而无法指向其他引用,这和其他语言中的指针并不相同。
  其实,两者都可以算是“值”传递,只不过“值传递”传递的是基本类型本来的值,而“引用传递”传递的则是复合对象的地址值。


值传递

案例一:

1
2
3
4
5
var a = 2;
var b = a;
b++;
b; //3
a; //2

上例说明基本类型的a的值在赋值给b时,是将2这个值赋给了b,此后b++不会改变a的值。

案例二:

1
2
3
4
5
var a = [1,2,3];
var b = a;
b.push(4);
b; //[1,2,3,4]
a; //[1,2,3,4]

上例说明复合类型的a在赋值给b时,是将2内存中[1,2,3,4]这个对象的引用赋给了b,因此a,b实际上操作的是一个地址中的内容。


引用传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function foo(x){
x.push(4);
x; //[1,2,3,4]

x = [4,5,6];
x.push(7);
x;
}

var a = [1,2,3];

foo(a);

a; //[1,2,3,4]

该例中函数foo()接收一个复合类型对象作为参数。按照引用传递的规则,可以理解为变量a所引用的对象[1,2,3]在内存中的地址被赋给了x,在foo()内部所做的操作,会直接影响到a所代表的值。但是,x = [4,5,6];相当于以字面量的形式将x所指向的内存中的地址进行了改变(一个新对象,其值为[4,5,6]),因此这部分不会体现在a中。


两者的“互相转换”

1.对于符合类型的,即对象,如果想按照值传递来实现某些功能(即不影响传入的实参本身),那么就要创建一个副本。比如对于数组,使用foo(a.slice()),通过slice()方法对a进行一次浅复制。

2.对于基本类型的,如果想按照引用传递的效果实现某些功能(即能够改变传入函数并将其本身进行修改),那么就要将该值封装到一个复合类型(数组,对象等)中,然后通过引用传递的方式进行传递。如:

1
2
3
4
5
6
7
8
function foo(obj){
obj.a = "new value";
}
var bar = {
a : "old value"
}
foo(bar);
bar.a; // new value

但是,这并不是说将基本类型装箱为对应的包装类型再传入函数就可以达到这种效果:

1
2
3
4
5
6
7
8
function foo(x){
x = x + 1;
}
var a = 2;
var b = new Number(a);
foo(b);
a; // 2
b; // 2

原因是foo()内部在进行加法操作时候对x进行了自动拆箱。x变成了基本类型值,但是外部的b仍然指向有Number()构造出的对象,因此是2,不是加1后的3。