1. 程式人生 > 實用技巧 >結合執行上下文理解js閉包、作用域及作用域鏈

結合執行上下文理解js閉包、作用域及作用域鏈

MDN上的定義

  • 函式和其對周圍狀態(詞法環境---作用域鏈)的引用捆綁在一起構成了閉包。
  • 每當函式建立時,就會在函式生成時形成閉包。
  • 內部函式可以訪問外部函式作用域

二、js中的作用域及作用域鏈---靜態作用域--詞法作用域--詞法環境

先來看一段幾乎都知道的白話文:作用域是變數和函式的可訪問範圍及生命週期。在當前作用域訪問變數,如果沒找到,就會去外層作用域查詢,一級級向上,直到最外層的window物件為止,這樣的層級關係便是作用域鏈。
在執行上下文中的體現:訪問變數、函式時,去當前執行上下文中的作用域鏈scope chain中查詢,scope chain的頂端為當前執行上下文的變數物件————對應著當前作用域。若沒有查詢到,則去外層變數物件中查詢,直到最外層的變數物件為止(即全域性作用域)。

引出下一個問題:執行上下文屬性中的scope chain是怎麼建立的?

  • 在函式定義建立時,會把其能訪問到的所有外層變數物件都儲存到函式的內部屬性[[scope]]
  • 建立執行上下文時,首先建立scope chain這個屬性,再把函式的內部屬性[[scope]]複製到scope chain中,但此時還並不是完整的作用域鏈,等到variable object變數物件建立完後,新增到scope chain的頂端,形成最終的作用域鏈。

得出重要結論:

  • 在函式建立時,就已經儲存好了其所能訪問到的所有外層的變數集合————也就是白話文中的靜態作用域,即函式的作用域在定義時就已經確定了。

MDN文件中閉包

官方文定義:函式和其對周圍狀態的引用捆綁在一起構成了閉包。
翻譯成白話文:函式和其作用域鏈上的變數物件構成了閉包。作用域鏈上的變數物件————即外層作用域下的變數。
閉包就是函式,對變數、函式的訪問,也一樣是從其執行上下文中的作用域鏈上查詢。
需要強調的是,作用域鏈的構成為當前執行上下文中的變數物件(即當前作用域下的變數及函式)和函式定義時所能訪問到的變數物件集合(即函式定義時的位置的作用域鏈)。

function fun(n,o) {
  console.log(o)
  return {
    fun:function(m){
      return fun(m,n);
    }
  };
}
var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined,?,?,?

第一條解析:
開始js程式碼的執行,瀏覽器建立一個全域性執行上下文。呼叫fun函式,var a = fun(0),在開始執行函式內部的程式碼前,瀏覽器解析器會建立一個函式執行上下文。並建立相應的執行上下文屬性scope chain,初始化函式的形參為undefined,因此var a = fun(0),會列印undefined,因為函式形參o在函式執行上下文的建立時被初始化為undefined
a.fun(1);a.fun(2);a.fun(3);,這三條語句各自建立了一個函式執行上下文。

簡單地進行分析:
var a = fun(0)

fun(0)_EC = {
      vo:{n:1,o:undefined}
}

所以列印了undefined
a.fun(1)

a.fun(1)_EC = {
      scope_chain:[{m:1}]
}

執行a.fun(1)時,內部也呼叫了fun(m,n)

EC={
      scope_chain:
}