JavaScript中引用类型的参数传递

ECMAScript中所有函数的参数都是按值传递的。可以把ECMAScript函数的参数想象成局部变量。

ECMAScript中所有函数的参数都是按值传递的。也就是说,函数的传参,就和基本类型变量的复制一样,而引用类型值的传递,则如同引用类型变量的复制一样。这里可能会使很多人感到困惑,因为访问变量有按值和按引用两种方式,而参数只能按值传递。

请看下面这个例子:

function add(num){
	num += 10;
    return num;
}

var count = 20;
var result = add(count);
console.log(count);	//20
console.log(num);	//30

在调用add()函数时,变量count作为参数被传递给函数。于是,数值20被复制给参数num。在函数内部,参数num的值被加上了10,但这一变化不会影响函数外部的count变量。

参数num与变量count互不相识,它们仅仅是具有相同的值。假如num是按引用传递的话,那么变量count的值也将变成30,从而反映函数内部的修改。

当然,使用数值等基本类型值来说明按值传递参数比较简单,但如果使用对象,那问题就不怎么好理解了。再举一个例子:

function setName(obj){
	obj.name = "Tom";
}

var person = new Object();
setName(person);
console.log(person.name);	//"Tom"

以上代码中创建一个对象,并将其保存在了变量person中。然后,这个变量被传递到setName()函数中之后就被复制给了obj。在这个函数内部,obj和person引用的是同一个对象

换句话说,即使这个变量是按值传递的,obj也会按引用来访问同一个对象。于是,当在函数内部为obj添加name属性后,函数外部的person也将有所反映;因为person指向的对象在堆内存中只有一个,而且是全局对象。

有很多人错误地认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。为了证明对象是按值传递的,我们再看一看下面这个经过修改的例子:

function setName(obj){
	obj.name = "Tom";
    obj = new Object();
    obj.name = "Pack";
}

var person = new Object();
setName(person);
console.log(person.name);	//"Tom"

这个例子与前一个例子的唯一区别,就是在setName()函数中添加了两行代码:一行代码为obj重新定义了一个对象,另一行代码为该对象定义了一个带有不同值的name属性。

在把person传递给setName()后,其name属性被设置为"Tom"。然后,又将一个新对象赋给变量obj,同时将其name属性设置为"Pack"。如果person是按引用传递的,那么person就会自动被修改为指向其name属性值为"Pack"的新对象。但是,当接下来再访问person.name时,显示的值仍然是"Tom"。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持未变。

实际上,当在函数内部重写obj时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。

再来看另一个例子,我们期望是将两个对象进行相互交换:

function swap(objOne, objTwo){		//这个的实例不能完成期望,不能使用此方法进行对象的交换。
    var obj = new Object();			
    obj = objOne;					//此时,obj,objOne,one三个对象变量的指针拥有相同的指向。
    objOne = objTwo;
    objTwo = obj;
}

var one = new Object();
var two = new Object();
one.name = "Tom";
two.name = "Pack";

swap(one, two);
console.log(one.name);	//“Tom”
console.log(two.nama);	//"Pack"

以上代码创建了两个对象,并将其保存在了变量one和two中。然后,这两个变量被传递到swap()函数中之后就被分别复制给了objOne和objTwo。在这个函数内部,objOne和one引用的是同一个对象,objTwo和two引用的是同一个对象。在swap()函数内部创建了一个新的对象obj用来交换objOne和objTwo这两个对象。但是,接下来访问one.name和two.name时,仍然显示的是原来的变量。

那是因为,在swap()函数传递参数时,使用的是值传递。而引用类型变量的值实际上是一个指针,这个指针指向存储在堆中的一个对象。在swap()函数内部进行交换时,只是将函数的量局部变量objOne和objTwo的值进行了交换,该操作并不影响函数外部one和two两个变量的值。因此,one和two这两个变量的值仍然保持不变,即这两个变量内部的指针仍然指向原来堆内存。

通过下面两张图片,我们可以清楚的看出函数执行前后,各个对象变量的指针指向。

  • 当代码执行到obj = objOne;时,各个对象的指针指向。
  • js 引用Jquery js 引用传参_按值传递

  • 当代码执行完毕时,各个对象的指针指向。
  • js 引用Jquery js 引用传参_javascript_02

可以看出,在函数执行前后,外部变量所存储的指针始终没有发生指向的改变,这也恰恰验证了我们最开始的结论:ECMAScript中所有函数的参数都是按值传递的。


如果觉得这片文章对你有所帮助,就请:点赞👍、收藏💗+关注⭐