在《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中,對象無論是不是當成參數傳遞,都是按引用傳遞的。當然這僅是我個人得出的結論,如果大家有不同的看法,非常願意向大家學習。