call by reference in javascript

 Tue, 13 Nov 2007 11:26:42 +0800

最近試做了一個csv/xml雙向轉檔的jscript(用到WSH)。為了提高程式的效率,所以嘗試了幾種方法,發現透過陣列的元素,或是物件的屬性,是可以達到call by reference的目的,但是直接把物件傳入函數,對物件本身的一些操作是沒有效果的。

以下是沒有效果的例子:

var a = "a test";
alert(a);
test(a);
alert(a);
function test(str) {
str += " more";
alert(str);
}

執行上例會依序跳出"a test"、"a test more"、"a test"三個alert對話框,在執行完test函數後,變數a(String物件的instance)並沒有改變。

但是不要以為完全做不到call by reference的效果,其實利用物件屬性或是陣列,可以達到目的。以下是物件屬性的例子:

var a = {"b":"a test"};
alert(a.b);
test(a);
alert(a.b);
function test(obj) {
obj.b += " more";
alert(obj.b);
}

執行上例,會依序跳出"a test"、"a test more"、"a test more"三個alert對話框,可見傳入的物件屬性真的被改變了,可以達到call by reference的效果。

使用陣列的例子:

var a = ["a test"];
alert(a[0]);
test(a);
alert(a[0]);
function test (arr) {
arr[0] += " more";
alert(arr[0]);
}

另外,使用物件的方法對物件進行操作,對於用call by reference是否有效呢?試試看:

var a = [];
alert(a.length);
test(a);
alert(a.length);
function test(arr) {
arr.push("a test");
}

在test函數中對傳入的陣列用push方法增加元素,可以成功進行。執行上例會跳出"0"、"1"兩個對話框,表示陣列元素增加了。

另一個可以操作的例子是Date物件:

var a = new Date();
alert(a.getTime());
test(a);
alert(a.getTime());
function test(date) {
date.setTime((new Date()).getTime());
}

透過setTime方法,在test函數就可以改變傳入的Date物件代表的時間。

使用者自訂的物件也有類似的效果:

var a1 = new a();
alert(a1.b);
test(a1);
alert(a1.b);
function a () {
this.b = "a test";
this.appendB = function (str) {
this.b += str;
}
}
function test (obj) {
obj.appendB(" more");
}

總結一下。傳入物件給函數操作時,如果操作的對象是物件本身(的primitive value?),不會有call by reference的效果,但是透過物件的方法或屬性來操作,則有call by reference效果。

(至於轉檔程式.....其實算是失敗,倒不是不能跑,而是用自己寫的node物件來產生xml,我要轉的檔案會產生兩萬個樹狀的nodes,處理起來實在太慢,只好改成用直接產生xml內容的方式做,但是這樣就不能因應xml結構的改變。也許改用flyweight來寫會好一點,有空再來試試。)