【JavaScript基礎】深入瞭解JS的閉包

    閉包是JS中一個重要的概念,也是面試中的重點,相信大家在面試中都會碰到這樣的幾個問題:

      1. 請你說說什麼是閉包?

      2. 請你說說閉包有哪些應用?

      3. 請你說說閉包有哪些優缺點?

    大家都是怎麼回答這幾個問題的呢?在網上的文章中對閉包的概念都有不同的說法,導致很多人對閉包沒有一個清晰的概念,接下來就和我一起探究什麼是閉包。

    首先看一下非常官方的解釋:函數和對其周圍狀態(詞法環境)的引用捆綁在一起構成閉包closure)。也就是說一個閉包函數和其詞法環境組成了閉包,如果對閉包沒有清晰的概念,這看起來似乎難以理解。我們可以有這樣一個簡單的概念:閉包函數是聲明在函數中的內部函數,內部函數可以引用其外部函數的變量,通過閉包可以使這種引用不會隨着外部函數壽命終止而銷燬。所以閉包可以成爲連接函數內部和外部的橋樑,使得其它函數可以使用函數內的變量。看到這裏可能還是一頭霧水,不着急,接下來讓我們來看一下JS的作用域概念以及閉包的產生。

    JS中的執行環境定義了一個關聯的變量對象,環境中定義的變量和函數都保存在這裏,每個函數都有一個執行環境,當函數執行時,函數的環境會進入棧中,執行完之後再彈出,交出控制權。當函數執行時,它的變量對象就是活動對象(AO),函數執行完AO對象就被釋放了,下次執行時會重新創建。在一個環境中執行時,會創建變量對象的一個作用域鏈,看下面這段簡單的代碼:

    下圖展示了這個例子的作用域鏈,其中最外層的作用域爲全局作用域,在Web瀏覽器中,全局執行環境爲 window 對象。圓形代表了一個特定的執行環境,內部作用域可以訪問外部作用域而外部則不能訪問內部作用域,這就是作用域鏈:一級一級地往上搜索。

 

    目前我們已經瞭解了JS的執行環境以及作用域鏈,下面就來通過一個簡單的例子創建一個閉包: 

    這裏外部函數將內部的 closure 函數返回形成閉包,通過執行 outer 函數將其賦值給 adder5 函數。outer函數執行完畢,我們知道,此時 outer 函數的活動對象 AO 已經被內存銷燬,普通情況下,其作用域下的 x 變量應該被垃圾回收機制回收了,但是這裏閉包函數的作用域鏈包含了 closure 內部的作用域、outer函數的作用域和全局作用域,這裏的 x 變量不會被垃圾回收機制回收,直至閉包的引用被銷燬。讓我們看一段示例代碼更深入瞭解閉包:

    可以看到 adder5 和 adder2 分別創建了兩個閉包,他們有相同的函數定義但是卻有獨立的執行環境,外部函數 adder 每執行一次就創建一個新的地址。

    重要概念:閉包取得的是同一地址外部函數中任何變量最後的值。

    下面來看兩個例子:

    

    

    瞭解了閉包之後,我們接下來看看閉包有哪些應用:

  • 實現工廠函數:如上面提到的 adder 函數,是一個實現了創建將傳入的參數和指定的值相加求和的函數。
  • 模塊化應用,摸擬私有方法:在其它面向對象的語言例如 java 中,可以創建一個私有方法,只允許被同一個類的其它方法所調用,原生 JS 不提供這種 API,但我們可以使用閉包來模擬私有方法。私有方法不僅僅有利於限制對代碼的訪問:還提供了管理全局命名空間的強大能力,避免非核心的方法弄亂了代碼的公共接口部分。如以下示例:    
  • 實用的例子:
    • 防抖:在一些按鈕中可能用戶在很短的間隔內多次點擊,此時會造成事件不必要地重複觸發,防抖使得在一定時間內連續調用的函數只執行一次。
    • 節流: 例如瀏覽器的滾動條 onscroll 事件等觸發頻率非常高,重複執行的代碼塊中可能包含 ajax 請求,節流的作用就是減少一些過高頻率的調用來節約資源,使得一段時間內核心代碼只執行一次。
    • 利用閉包實現多個用於改變某一字號的函數:

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章