關於無處不在的閉包

閉包是函數和函數的詞法環境的引用的組合 [MDN]
閉包是指有權訪問另外一個函數做用域中的變量的函數 [JS高級程序設計]
閉包是內部函數引用外部函數的變量的集合 [李兵]
閉包是一個綁定了執行環境的函數 [程劭非]

1. 什麼是閉包

欲懂閉包,必先知道做用域、做用域鏈的含義。那麼咱們就從做用域開始講起。html

(1)做用域 = 去哪裏找變量

函數執行時會造成調用棧,調用函數時其執行上下文(其中保存了:變量環境和詞法環境)入棧,執行完畢出棧。執行上下文存儲着函數中聲明的變量,函數執行也會訪問其餘函數中聲明的變量。在函數執行查找變量時,變量的可見範圍就是做用域,查找變量的鏈路就變量的做用域鏈
查找變量時遵循兩個規則:第1,函數嵌套時內部函數總能訪問外部函數中聲明的變量;第2,調用棧中的函數,老是先在自身的執行上下文找變量,再到最外層的全局執行上下文去找數組

(2)閉包 = 函數 + 變量

定義: 持有着其它做用域中變量的函數 及其持有的其它變量的集合 的組合
  • 閉包產生的過程:閉包一般產生於函數嵌套時。外部outer函數聲明瞭變量o,內部函數inner能夠訪問到o。當inner函數被做爲返回值被outer返回, outer函數執行完畢以後,outer的執行上下文就彈出了調用棧。此時變量o應該被GC回收掉,但因爲inner函數此時還引用時變量o, o並無隨着outer的執行完畢而銷燬,此時咱們稱產生了閉包。
  • 閉包產生的緣由:即便外部函數已經執行完,因爲內部函數持有外部函數中變量的引用,外部函數做用域的變量依然存在在內存之中
產生閉包以後,變量的查找鏈路就會發生變化。首先在自身的執行上下文找變量,再到閉包中去找,最後再到最外層的全局執行上下文去找

2. 閉包的特徵

(1)函數的返回值是函數
(2)函數做爲參數傳遞給另外的函數閉包

回調函數都是閉包,由於函數被當作參數傳遞了

3. 閉包的使用場景

根據上述第2點,閉包的特徵仍是比較明顯的。本節介紹閉包的具體應用場景。
(1)全部使用到回調函數的地方都是閉包的應用,如事件監聽回調、Promise綁定的then回調、數組的map()函數;
(2)利用閉包模仿塊級做用域、封裝私有變量、延長局部變量的壽命;
(3)閉包和setTimeOut()結合實現防抖、節流;
(4)函數柯里化:多參數函數轉化成單參函數,好處是能夠進行參數複用。函數

// 普通的add函數
function add(x, y) {
    return x + y
}
// Currying後
function curryingAdd(x) {
    return function (y) {
        return x + y
    }
}
add(1, 2)           // 普通函數調用3
curryingAdd(1)(2)   // 柯里化函數調用3

4. 注意事項

閉包會攜帶包含其它函數的做用域,所以會比其餘函數佔用更多的內存,容易致使內存泄漏。若是閉包會一直使用,那麼它能夠做爲全局變量而存在;但若是使用頻率不高,並且佔用內存又比較大的話,那就儘可能讓它成爲一個局部變量。post

總結

本文以變量的查找做爲出發點,引出了變量做用域、做用域鏈、函數的調用棧等概念。而後介紹了閉包的組成、造成過程。閉包聽起來玄妙,用起來簡單。文中還總結了閉包的典型特徵,有助於咱們在實際的開發過程當中發現閉包、理解閉包。最後,還介紹了閉包的具體使用場景,也給出了其缺點、使用注意事項。設計

參考:
https://juejin.cn/post/694469...
https://www.cnblogs.com/gg-qq...
https://time.geekbang.org/col...code

相關文章
相關標籤/搜索