再看幾個Closure的小測試

 Wed, 17 Dec 2008 23:11:28 +0800

上一篇文章中,對於除了null以及undefined之外的型別做了一些測試,這幾天有用同樣的closure函數做一點深入的測試來試探他的效果。也很有趣。

主要的測試如下:

var changeObj = function(v) {
v.a = "var ? attrib altered";
}
var closureObj = function(v) {
return function() {
return v;
}
}
var a = {a:"var a attrib a", b:"var a attrib b"};
var b = {a:"var b attrib a", b:"var b attrib b"};
var a1 = closureObj(a);
var b1 = closureObj(b);
a = null;
b = null;
alert(a1().a);
alert(b1().a);
changeObj(a1());
changeObj(b1());
alert(a1().a);
alert(b1().a);
closureObj = null;
changeObj = null;
alert(a1().a);
alert(b1().a);

測試結果分別會跑出:

  1. var a attrib a
  2. var b attrib a
  3. var ? attrib altered
  4. var ? attrib altered
  5. var ? attrib altered
  6. var ? attrib altered

可以看到幾個效果:

  1. 每次呼叫closureObj產生的函數,都會各自保存各自的Context,即使closureObj函數沒作用了也一樣
  2. closureObj產生的函數返回的物件透過changeObj修改過就改變了下一次呼叫這個函數返回的物件

ECMA-262 Edition 3定義了一個Reference物件(下面稱作參考),所有的Assignment動作都要運作得像利用這個物件(實作上並不需要真正做出這個物件,只要表現得像有這個物件存在)。簡單地說,這個地方很像Java。

var a = {a:"var a attrib a", b:"var a attrib b"};
在這裡,Object Literal產生了一個物件,然後把Reference assign給變數a。
var a1 = closureObj(a);
變數a1是一個函數,裡面有一個對{a:"var a attrib a", b:"var a attrib b"}物件的參考,這是透過closureObj的參數傳進去,然後保留在a1參考到的函數物件裡的。
a = null;
這樣也只是讓a變數不再保留對{a:"var a attrib a", b:"var a attrib b"}物件的參考,但是透過closureObj,a1變數參考到的函數物件裡面其實還是保存了一份對{a:"var a attrib a", b:"var a attrib b"}物件的參考,所以只要不另外assign其他的東西給a1變數,{a:"var a attrib a", b:"var a attrib b"}物件就仍然會有參考存在,所以不會消失。而
closureObj = null;
其實只是讓closureObj變數不再參考到那個函數物件,但是其實a1跟b1變數仍然有對這個函數的參考,所以仍然可以正常使用。

另外:

changeObj(a1());
透過a1()傳給changeObj函數的也是一個參考,所以在changeObj函數中透過這個參考修改{a:"var a attrib a", b:"var a attrib b"}物件,這個物件就永遠改變了,下次呼叫a1()時,傳回的就是這個改變了的物件的參考。

基本上,這個部份用java的「參考」概念來概括,應該就差不多了吧?