JS的閉合(Closure)

 

定義

MDN:函數和對其周圍狀態(lexical environment,詞法環境)的引用捆綁在一起構成閉包(closure)。

JS高程: 閉包是指有權訪問另一個函數作用域中的變量的函數。

Javascript權威指南:函數對象可以通過作用域關聯起來,函數體內的變量都可以保存在函數作用域內,這在計算機科學文獻中稱爲“閉包”,所有的javascirpt函數都是閉包

你不知道的JS(上):當函數可以記住並訪問所在的詞法作用域時,就產生了閉包,即使函數是在當前作用域外執行

瀏覽器原理與實踐:當通過調用一個外部函數返回一個內部函數後,即使該外部函數已經執行結束了,但是內部函數引用外部函數的變量依然保存在內存中,我們就把這些變量的集合稱爲閉包。比如外部函數是 foo,那麼這些變量的集合就稱爲 foo 函數的閉包。

各種書籍上對閉包的定義各不相同,但是我們沒必要太過於糾結概念,只要我們深入理解他的含義和使用就可以了

產生原因

閉包產生與如下兩個方面有關

  • 根據詞法作用域的規則,內部作用域可以訪問外部作用域
  • GC(垃圾回收)機制,如果變量被引用那麼GC在回收時並不會回收該變量

實例

    function foo() { 
        let test1 = '變量1'
        const test2 = '變量2'
        let test3 = '變量3'
        var innerBar = { 
            getName: function () { 
                console.log(test1) 
                return test2 
            },
        } 
        return innerBar 
    } 
    var bar = foo()
    bar.getName()

 

我們可以通過控制檯的Source中的scope看到閉包

image.png

右邊 Scope 項就體現出了作用域鏈的情況:Local 就是當前的 getName 函數的作用域,Closure(foo) 是指 foo 函數的閉包,最下面的 Global 就是指全局作用域,從“Local–>Closure(foo)–>Global”就是一個完整的作用域鏈。

注意⚠️:只有我們在內部函數中使用的變量纔會被加入閉包(Closure)中。

我們來看下範例中產生閉包時執行棧的情況:

  • 當執行到 foo 函數內部的return innerBar這行代碼時調用棧的情況

image.png

  • 當 foo 函數執行完成之後,其整個調用棧的狀態(注意⚠️:此時foo執行上下文已出棧)

image.png

  • 當執行到bar.getName()時的棧狀態

image.png

通過上面三個圖我們可以清晰的看到當前作用域鏈 是Local–>Closure(foo)–>Global。

用途

實現私有變量

function Animal( ){

    //私有變量
    var series = "哺乳動物"function run( ){
        console.log("Run!!!");
    }   

    //特權方法
    this.getSeries = function(){
        return series;
    };
}   

實現模塊模式

var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }   
})();

console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */

缺陷

如果引用閉包的函數是一個全局變量,那麼閉包會一直存在直到頁面關閉;但如果這個閉包以後不再使用的話,就會造成內存泄漏。(如果引用閉包的函數是個局部變量,等函數銷燬後,在下次 JavaScript 引擎執行垃圾回收時,判斷閉包這塊內容如果已經不再被使用了,那麼 JavaScript 引擎的垃圾回收器就會回收這塊內存)

規避:如果該閉包會一直使用,那麼它可以作爲全局變量而存在;但如果使用頻率不高,而且佔用內存又比較大的話,那就儘量讓它成爲一個局部變量

 

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