前言 :
所謂的閉包(Closure),是指一個函式物件(或函式值)在建立時,綁定了當時作用範圍(Scope)下有效的自由變數(Free variable)。所 以支援閉包的語言,必須有支援一級函式(First-class function),建立函式物件並不等於建立閉包,建立函式物件時同時綁定了某個(些)自由變數,該函式物件才稱之為閉包.
那麼什麼是自由變數?自由變數是指對於函式而言,既非區域變數也非參數的變數,像區域變數或參數,其作用範圍基本上是在函式定義的範圍中,所以是有界變數(Bound variable)。舉個例子來說 :
上面 doSome 的例子中,f 形成了一個閉包,如果你單看 :
- function f(y) {
- return x + y;
- }
由於 doSome 函式傳回了函式物件(函式是物件,當然也就可以當作結果傳回),上例中將傳回的函式物件指定給 foo,就 doSome 而言已經執行完畢,單看 x 的話,理應 x 已結束其生命週期,但由於 doSome 中建立了閉包並傳回,x 被關閉在閉包中,所以 x 的生命週期就與閉包的生命週期相同了,如上例所示, 呼叫 foo(20) 結果就是10+20(因 為被閉關的 x 值是10),呼叫 foo(30) 結果就是10+30.
注意!閉包關閉的是自由變數,而不是自由變數所參考的值.下面這個範例可以證明 :
建立閉包時,綁定了 x 變數,而不是數值10(x 變數的值),也因此 doOther 之後改變了 x 變數的值,而後傳回閉包給 foo 參數後,範例顯示的結果分別是 100+2 0與 100+30. 由於閉包綁定的是變數,所以你也可以在閉包中改變了變數值 :
你可能會有疑問的是,如果閉包關閉了某個自由變數,使得該自由變數的生命週期得以延長,那麼這個會怎麼樣 ?
在範例中,doOther 被呼叫了兩次(或更多次),doOther 中的閉包關閉了 x,並對其執行了遞減。呼叫了 f1 時,x 會被遞減1,所以顯示 9,這沒有問題,那麼呼叫 f2 後,結果還是9! 像這類的例子,其實結果是很一致的,關閉的是建立閉包時有效範圍下的自由變數. 以上例來說,第一次呼叫 doOther 時,建立了 x 變數,指定值給 x 變數,而後建立閉包將之關閉。第二次呼叫 doOther 時,建立了 x 變數,指定值給 x 變數,而後建立閉包將之關閉。所以 f1 與 f2 關閉的根本是不同作用範圍的 x 變數!(也就是該次呼叫 doOther 時所建立的 x 變數).所以上例中,呼叫 f2 之後顯示的值仍是 9. 下面這個也是個類似的例子 :
閉包應用 :
閉包的實際應用很多,例如,在 因式分解 中,可讓閉包綁定質數表,之後就不用重複建立質數表 :
primes 被閉包綁定,所以 primes 所參考的物件自然也就被綁定. 你可以這麼使用 :
閉包也會用來作為物件私用(private)的模擬,以及名稱空間的管理等,這之後還會再看到說明.
沒有留言:
張貼留言