在《JavaScript高级程序设计》这本书中有这样一段话:有很多开发人员错误的认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。换句话说,尼古拉认为当一个对象是当做参数传递时,它是按值传递的。然后他举了个例子来证明这个结论:
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); // "Nicholas"
他解释到:如果person
是按引用传递的,那么person
就会自动被修改为指向其name
属性值为"Greg"
的新对象。但是,当接下来再访问person.name
时,显示的值任然是"Nicholas"
。这说明即使在函数内部修改了参数的值,但原始的引用任然保持不变。实际上,当在函数内部重写obj
时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。
从上面的例子中,尼古拉得出的结论是:当一个对象当做参数传递时,它是按值传递的。
然而,我们可以肯定的是:当一个引用类型的对象不是当做参数传递时,它是按引用传递的。
我们来看另外一个例子:
var person = new Object();
var obj = person;
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
alert(person.name); // "Nicholas"
这个例子中person
对象不是当做参数传递的,但person.name
的值还是"Nicholas"
,这和person
当做参数传递的情况是一样的。可以确定的是:这个例子中对象是按引用传递的。但按照尼古拉的说法这个例子中对象也是按值传递的。 那么可以得出结论:尼古拉的说法是错的。
我们可用图来说明一下这个问题。
当var person = new Object()
时,可以用下面这幅图来描述变量和对象之间的关系:
当var obj = person
时,可以用下面这幅图来描述它们之间的关系:
当obj = new Object()
时,用下面的图描述它们的关系:
我们可以把Object
当成一个中间人,它是联系person
与obj
的桥梁。之所以改变obj
的属性值,会影响person
其实是通过Object
来传递的。当obj = new Object()
,这时obj
与Object
之间的关系完全断开,与new Object
建立了关系。当obj.name = "Greg"
,此时,obj
与"Object"
之间已没有关系,当然不会再影响"Object"
。
书中的例子是一样的道理:
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); // "Nicholas"
obj
相当于setName
函数中的一个局部变量,把person
传给函数就相当于var obj = person
。其实我们完全可以把这个例子和上面介绍的那个例子看成是等价的,所以上面的图解也适用这个例子。
上面通过例子来说明尼古拉的结论是错的,从而得出自己的结论:ECMAScript中,对象无论是不是当成参数传递,都是按引用传递的。当然这仅是我个人得出的结论,如果大家有不同的看法,非常愿意向大家学习。