js按值传递还是按引用传递?

Posted Likebard

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js按值传递还是按引用传递?相关的知识,希望对你有一定的参考价值。

js和其他大部分语言一样,有基本类型和引用类型。因此访问变量就有按值按引用两种方式,但是传参的时候却只能按值传递。基本类型作为参数时按值传递自然无可厚非,但引用类型作为参数也按值传递就让人有点困惑了。

看下面这个例子:

 1 function setName(obj){
 2 
 3   obj.name = "Sim";
 4 
 5 }
 6 
 7 var person = new Object();
 8 
 9 setName(person);
10 
11 alert(person.name);  //"Sim"

以上代码创建了一个名为person的对象,并且将其作为参数传给了setName函数。我们在函数内部给传进来的对象添加了一个name属性并赋值,之后在函数外部访问person.name,会弹出"Sim"。一般我们都说,若是按值传递的话,形参在函数内的变化不会影响到实参的变化,但这个例子中,函数内部给形参加上的属性在实参中也有此属性。这会让人产生一种误解,感觉引用类型传参貌似是按照引用传递的啊!

下面再给出一个例子:

var obj1 = {
  value:\'111\'
};
 
var obj2 = {
  value:\'222\'
};
 
function changeStuff(obj){
  obj.value = \'333\';
  obj = obj2;
  return obj.value;
}
 
var foo = changeStuff(obj1);
 
console.log(foo);// \'222\' 参数obj指向了新的对象obj2
console.log(obj1.value);//\'333\'

上面例子打印出了形参obj和实参obj1的value值我们可以清楚的看到,这两个value值并不一样,因此可以证明引用类型的参数并非是按照引用传递的。

至于为什么会出现这种情况,那就有必要来理解一下js中的数据存储与访问的机制了。

声明变量时的内存分配

基本类型:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。这是因为这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 – 栈中。这样存储便于迅速查寻变量的值。

引用类型:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存地址。这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。

复制变量时的不同:

 

基本类型:在将一个保存着原始值的变量复制给另一个变量时,会将基本类型的副本赋值给新变量,此后这两个变量是完全独立的,他们只是拥有相同的value而已。

引用类型:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。(这里要理解的一点就是,复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)
 
在清楚了这些之后,我们再来结合之前的那个例子来看:
可以看到之所以给形参加上属性之后,实参也会带有此属性。那是因为实参,形参都是指向储存在堆内存中的对象的指针。我们改变的其实是堆内存中的对象的属性,obj和obj1都没有发生改变。但当执行了obj = obj2之后,相当于将obj指向了obj2所指向的value值为‘222\'的对象。
搞清楚了这些,估计你就能理解为什么引用类型的传参方式是按值传递了吧!

以上是关于js按值传递还是按引用传递?的主要内容,如果未能解决你的问题,请参考以下文章

js传参是按值传递还是按引用传递?

JS基础类型和对象,分别是按值传递还是按引用传递?

Java的参数传递是「按值传递」还是「按引用传递」?

JavaScript 是按引用传递还是按值传递? [复制]

java中的参数传递是按引用传递还是按值传递

JavaScript 是按引用传递还是按值传递语言?