【舊文新讀】解釋“閉包”需要幾行程式碼?

天方夜發表於2017-10-19

新讀(2017年10月19日)

本文寫於 2013年06月16日,今天做了一點修改,所謂修改,其實只是刪去了幾句不影響技術內容的話。關於閉包,我最近寫了一篇新的文章,提到了靜態作用域,相比本文,是對閉包的更深一層的解釋,可以作為本文的後續閱讀。至於本文,也不是已經沒有閱讀的價值,因為它說明了以下兩點:

  1. 閉包是一個概念,JavaScript 程式設計中常常用到閉包,但僅僅學會使用閉包,並不等於知道閉包本身是什麼;而本文,正是從概念本身解釋閉包。
  2. 理解閉包與物件的關係,對於理解閉包非常有幫助,因為二者的功能有很大的重疊。

以下是舊文

關於什麼是閉包,很多“高手”會給出一段“特權函式”訪問外部作用域變數的 JavaScript 程式碼,並且告訴你,閉包就是區域性變數在函式返回之後繼續存在。那麼,你如何理解“閉包是窮人的物件”、“物件是窮人的閉包”?

===========================================================

  • 以下內容根據《程式碼的未來》、“犀牛書”、以及《Thinking in Java》整理

===========================================================

什麼是閉包?

閉包(Closure)這個詞的意思是封閉,將外部作用域中的區域性變數封閉起來的函式物件稱為閉包。被封閉起來的變數與封閉它的函式物件有相同的生命週期。

什麼是函式物件?

函式物件是作為物件來使用的函式,這裡的物件是指程式語言操作的資料。

函式物件與閉包

函式物件不一定是閉包。

C 語言中,可以獲取一個函式的指標,並通過指標間接呼叫此函式。這就是 C 語言中的物件(函式物件也是物件)。但 C 語言中的函式物件不是閉包——它不能訪問外部作用域的區域性變數。

JavaScript 中,每個函式都有一個與之相關聯的作用域鏈。每次呼叫 JavaScript 函式的時候,都會為之建立一個新的物件用來儲存區域性變數,並把這個物件新增至作用域鏈中。當函式返回時,再將這個物件刪除,此物件會被當做垃圾回收。但如果這個函式定義了巢狀的函式,並將它儲存在某處的屬性裡,就意味著有了一個外部引用指向這個巢狀的函式。它就不會被當作垃圾回收,它所指向的變數繫結物件同樣不會被回收。

由此可見,JavaScript 中的函式物件是閉包——可以把外部作用域的區域性變數“封閉”起來。

什麼是物件?

物件導向中的“物件”是指問題空間中的元素(貓、狗)及其在解空間中的表示(new Cat(); new Dog())。物件是過程(函式)與資料的結合。

物件與閉包

物件是在資料中以方法的形式內含了過程,閉包是在過程中以環境的形式內含了資料。所謂“閉包是窮人的物件”、“物件是窮人的閉包”,就是說使用其中的一種方式,就能實現另一種方式能夠實現的功能。

應用場景

保護函式內的變數安全:如迭代器、生成器。 在記憶體中維持變數:如快取資料、柯里化。

相關文章