關於 Closure 和 Anonymous function 的差別
jaceju 在 Anonymous functions in PHP 說某個 PHP 研討會討論了匿名函數 (anonymous function) 在 PHP 中的需求性。 jaceju 注明 Jim Wilson 說匿名函式和 closure 是完全不一樣的東西,而他自己看不出兩者的差別。
我在寫 JavaScript 時,常常碰到這個問題。用 JavaScript 也比較容易說明兩者的差異。
JavaScript 語法支持匿名函數(Anonymous function)[參考The practice of anonymous recursion function in JavaScript以了解匿名函數在 JavaScript 中的應用],但不支持封絕(Closure)[我稱為封絕,也有人稱它閉鎖空間或閉包,還是記住 Closure 這個詞吧]。 JavaScript 必須使用一些技巧實現 Closure ,因此我們反而容易看出這兩者的差別。
建議使用 JavaScript shell 執行上面這段程式碼。若有人想用 .Net 的 JScript compiler (jsc.exe) 編譯再執行,也可以啦。其結果如下所示:
Pure anonymous function 3 3 3 Closure 0 1 2
第一段 (line 3~7) 實際上是在陣列 fs 中儲存了 3 個純匿名函數的參照,這3個函數參照應該指向同一段程式碼 (content)。當它們執行 print(i);
時,使用的是第1行定義的 i 。此時, i 之值為 3 ,故3個函數的執行結果都是 3 。
第二段 (line 17~20) 的寫法就不一樣了,此一寫法產生了一個 Closure 。 Closure 擁有一個獨立的 content 儲存其中的局部內容。故在此例中的 i 實際上是儲存在一個與外界隔離的 content 之中的區域變數,並不是第一行所定義的 i。同樣在此 Closure 之中的匿名函數,將使用這個被隔離的區域變數 i。由於此時這3個獨立的content中的 i 之值不同,故每次函數的執行結果都不同。
順便提一下記憶體回收的事。在第二段定義並執行了3次匿名函數實例。這3個匿名函數實例各自產生了一個獨立的 content 。一般情形時,執行之後其 content 內容就會被系統收回。但在此例中,我又在這 content 中定義了一個匿名函數 (第19行),並將之回傳後儲存在 fs 陣列。由於 content 中的資源 - 即其中的匿名函數 - 被尚存在的資源 fs 所指涉,故不會被系統回收。直到 fs 消逝之後,系統才會收回此例所產生的3個獨立 content 佔有的記憶體。
補充一份參考文件。由 Martin Fowler 寫的 《Closure》。Fowler 也寫了一個 JavaScript 的例子。因為他寫的例子有點長,我改了一個簡潔版,如下所示:
我的例子是用匿名函數創造 Closure ,而 Fowler 的例子則是用具名函數 paidMore() 創造 Closure 。
樂多舊回應