JavaScript - 閉包

moreor發表於2018-07-05

部分概念內容來源於《JavaScript高階程式設計(第3版)》

1.概念

閉包是指有權訪問另一個函式作用域中的變數的函式。【說白了,閉包就是一種特殊的函式】建立閉包的常見方式,就是在一個函式內部建立另一個函式。

一般來講,當函式執行完畢後,區域性活動物件就會被銷燬,記憶體中僅儲存全域性作用域(全域性執行環境的變數物件)。但是閉包的情況又有所不同。

2.例項

這裡舉一個程式防抖的例子,之所以舉這個例子,是因為對程式防抖程式印象比較深刻,因為按之前的理解,一個方法執行結束後,該方法內定義的變數函式就會立即被銷燬。防抖程式debounce()執行結束後,裡面的變數fn,delay,timer理論上都已經不存在了,如果在外部執行debounce()返回的匿名函式,就會報錯,但是實際上,匿名函式在debounce()的外部依然可以使用這三個變數。

/**
 * 防抖程式要考慮的兩個問題:
 *  1. 執行環境中this,以及接受的引數問題;
 *  2. 如何判斷上一個防抖函式已經執行完成;
 * 
 * 解決方案:
 *  1. 通過獲取執行環境的 this 和 arguments
 *      使用apply方法執行原始函式:fn.apply($this, args);
 *  2. 直接清除上一個計時器clearTimeout
        因為如果上一個計時器沒有計時結束說明在單位 delay 時間內該函式執行的兩次;
        如果已經計時結束執行完,說明未多次執行該函式
    * 
    **/
function debounce(fn, delay) {
    let timer = null; // 定義一個計時器
    
    return function() {
        let $this = this; // 被設定防抖的函式的執行環境的this
        let args = arguments;
        if (timer) {
            clearTimeout(timer);
            timer = null;
        }
        timer = setTimeout(function() {
            fn.apply($this, args);
        }, delay);
    };
}
複製程式碼

3.說明

在匿名函式從debounce()中被返回後,它的作用域鏈被初始化為包含debounce()函式的活動物件和全域性變數。這樣匿名函式就可以訪問在debounce()中定義的所有變數了。更重要的是,debounce()函式在執行完畢後,其活動物件也不會被銷燬,因為匿名函式的作用域鏈仍然在引用這個活動物件。換句話說,當debounce()函式返回後,其執行環境的作用域鏈仍然在引用這個活動物件,但它的活動物件仍然會保留在記憶體中;直到匿名函式被銷燬後,debounce()的活動物件才會被銷燬。

相關文章