7.完全搞懂javascript-閉包

關於閉包的討論的太多太多了,這裏不講晦澀難懂的概念。bash

咱們來想一個問題,閉包

如何把函數的運行上下文保存下來?函數

啥?ui

function print() {
    var inFunction = 'inFunction';
    console.log(inFunction);
}

print()
複製代碼

咱們知道在進入函數運行的時候,會建立函數的運行上下文,並放在棧頂:spa

當函數運行完畢,把函數運行上下文從運行棧中彈出:3d

你們會發現,對上次運行的函數時建立的詞法環境的訪問鏈路,斷掉了,咱們找不到了,沒法再引用函數詞法環境裏的變量(上圖中紅色框框所示)。code

那有沒有辦法,把這個對這個詞法環境的引用保存下來呢?cdn

答案是有的。對象

咱們回憶一下:函數建立時會把建立函數時候的運行上下文的詞法環境保存在函數對象的[[scope]]中。所以,若是咱們在函數運行的上下文建立一個函數,這個函數的[[scope]]就能夠保存父級函數運行的時的詞法環境了。像這樣:blog

function print() {
    var inFunction = 'inFunction';
    console.log(inFunction);
    function inner() {
        var inFunction = inFunction;
    }
}

print()
複製代碼

如上圖,發現雖然能夠經過內部函數的[[scope]]其訪問 "失聯"的詞法環境,咱們發現,內部函數因爲外部函數運行上下文的退出,也處於「失聯」狀態,咱們在全局上下文上沒法訪問內部函數。

其實很直觀的,若是在全局上建立一個變量,並把內部函數返回給它,它不就能夠經過全局變量訪問內部函數了,進而能夠訪問外部函數那些「失聯的詞法環境」。

function print() {
    var inFunction = 'inFunction';
    console.log(inFunction);
    function inner() {
        var inFunction = inFunction;
    }
    
    return inner
}

var inner = print();
inner();
複製代碼

以下圖所示:

咱們發現,即便函數運行上下文退出了,咱們在全局環境中仍是能夠訪問到所退出的函數的詞法環境。

更近一步思考:既然能夠在函數內部返回一個函數對象給全局變量,能夠保持對已經退出運行棧的運行上下文的詞法環境的訪問,若是咱們在函數內返回一個普通對象呢?把須要被全局環境訪問的「東西」複製到對象上,而後把對象返回,咱們也能夠在全局變量經過對象找到那些「失聯」的信息。

function print() {
  var inFunction = 'inFunction';
  console.log(inFunction);
  function inner() {
      var inFunction = inFunction;
  }
  
  return {
    inFunction:inFunction,
    inner:inner
  }
}

var obj = print();
console.log(obj.inFunction);
複製代碼

這就是閉包。

因此閉包,其實就是經過某種方式把把「失聯」詞法環境的引用引到全局環境來。由於這詞法環境如今只有外圍函數返回時提供的引用做爲入口,只有一個洞能夠進去,是一個密閉的空間,故而叫作閉包吧。

閉包,可讓函數在運行完畢後,其運行時的上下文的詞法環境仍然能被訪問。

相關文章
相關標籤/搜索