Js中按值传递和按引用传递
文章思路:
js的数值类型(基本类型,引用类型)--数组是引用类型?--对象的参数传递--面试题
在介绍Js的赋值方式时首先要说明js的数值类型:基本类型和引用类型
1.基本类型
基本的数据类型有:undefined,boolean,number,string,null。基本类型存放在栈区,访问是按值访问,就是说你可以操作保存在变量中的实际的值
当基本类型的数据赋值时,赋的是实际的值:
var a = 10,b=a;
a=20;
a == > 20;b ==> 10
说明:a和b是没有关联关系的,b由a赋值得到,相互独立
2.引用类型
引用类型指的是对象,可以拥有属性和方法,并且我们可以修改其属性和方法。引用对象存放的方式是:在栈中存放对象变量标示名称和该对象在堆中的存放地址,在堆中存放数据。
对象使用的是引用赋值。当我们把一个对象赋值给一个新的变量时,赋的其实是该对象在堆中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
比如:
var obj = { x = 1}
var obj2 = obj;
obj.x = 3;
obj.x == > 3
obj2.x ==> 3
3.数组是引用类型
我们先来看一个例子:
var a = [1,2,3]
var b = a;
a = [4,5,6];
b ==> [1,2,3]
难道数组是基本类型??别急,我们再看看一个例子
var a = [1,2,3];
var b = a;
a.pop();
a ==> [1,2]
b ==> [1,2]
这是怎么回事呢?
以下是来自于一个知乎大神的解释:
a = [4,5,6] -- 改变的是a引用本身,没有改变数组对象,a和b没有关系
a.pop() -- 改变的是数组对象,a引用没有变。
b = a; -- 该操作后,b直接指向数组对象,不是b指向a,a再指向数组。所以改变a引用并不会堆b引用造成影响,改变数组对象可以
4.参数传递
对于js来说,基本类型是按值传递的:
举例:
var a = 1;
function foo(x){
x = 2;
}
foo(a)
a ==> 1 //仍然是1,没有受到x=2的影响
所以:按值传递就是复制了份a内容给x而已,a和x之间没有关系
再来看看对象
var obj = {x:1}
function foo(o){
o.x=3;
}
foo(obj)
obj.x ==> 3 //被修改了!!!
这样是否说明JS的对象是按引用传递的呢?我们再来看看下面的例子
var obj = {x:1}
funtion foo(o){
o = 100;
}
foo(obj)
obj.x ==> 1 //仍然是1,并没有被修改
如果是按引用传递,修改形参o的值,应该影响到实参才对。但这里修改o的值并未影响obj。 因此JS中的对象并不是按引用传递。那么究竟对象的值在JS中如何传递的呢?
答案是:JS的基本类型是按值传递,对象类型是按共享传递的,该方法的重点是:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)。 它和按引用传递的不同在于:在共享传递中对函数形参的赋值,不会影响实参的值。如下面例子中,不可以通过修改形参o的值,来修改obj的值。
简单的说,就是,引用类型中,函数接收到的是实参引用的副本,所以对形参的赋值,不会影响到实参的值,就正如上面的例子,不能通过修改形参o的值来修改obj的值
然而,虽然引用的是副本,引用的对象也是相同的。他们共享相同的对象,所以修改形参对象的属性值,也会影响到实参的属性值
就比如:
var obj = {x : 1};
function foo(o) {
o.x = 3;
}
foo(obj);
console.log(obj.x); // 3, 被修改了!
最后我们看一个js面试题:
var a = 1;
var obj = {
b: 2
};
var fn = function () {};
fn.c = 3;
function test(x, y, z) {
x = 4;
y.b = 5;
z.c = 6;
return z;
}
test(a, obj, fn);
alert(a + obj.b + fn.c);
首先test传递进去的实参中,a是基本类型(,复制了一份值),obj是object(指向地址,你动我也动),fn也当然不是基本类型啦。在执行test的时候,x被赋值为4(跟a没关系,各玩各的,a仍然为1),y的b被赋值为5,那obj的b也变为5,z的c变为6,那fn的c当然也会是6. 所以alert的结果应该是1+5+6 =12. (其实test不返回z也一样,z仍然改变的)