前端進階之你真的瞭解JS的做用域跟上下文嗎?

  • 做者:陳大魚頭
  • github: KRISACHAN
  • 連接:github.com/YvetteLau/S…
  • 背景:最近高級前端工程師 劉小夕github 上開了個每一個工做日佈一個前端相關題的 repo,懷着學習的心態我也參與其中,如下爲個人回答,若是有不對的地方,很是歡迎各位指出。

詞法環境

詞法環境(Lexical Environment) 就是 ECMAScript 的代碼環境。javascript

它由 環境記錄(Environment Record)外部引用 組成。前端

它會根據 ECMAScript 代碼來動態建立。java

環境記錄綁定了其關聯的詞法環境。其一共分了5類,分別以下:git

  • 聲明環境記錄(Declarative Environment Records)聲明環境記錄(Declarative Environment Records) 綁定變量聲明,其中包含:varletconstclassmoduleimportfunction
  • 對象環境記錄(Object Environment Records)對象環境記錄(Object Environment Records) 記錄每一個對象增刪改查。
  • 函數環境記錄(Function Environment Records)函數環境記錄(Function Environment Records) 每一個 函數做用域 以及 上下文環境的具體狀況。
  • 全局環境記錄(Global Environment Records)全局環境記錄(Global Environment Records) 用於表示全部 ECMAScript Script元素 共享的 最外層做用域 。全局環境記錄提供內置全局變量,全局對象的屬性以及腳本中發生的全部頂級聲明的綁定。
  • 模塊環境記錄(Module Environment Records)模塊環境記錄(Module Environment Records) 用於表示 ECMAScript模塊 的外部範圍以及綁定狀況。

詞法環境分了三類:github

  • 全局環境(global environment)全局環境(global environment) 是一個沒有外部環境的 詞法環境全局環境(global environment)的外部環境引用爲 null全局環境(global environment)EnvironmentRecord(環境記錄) 能夠綁定變量,並關聯對應的 全局對象
  • 模塊環境(module environment)模塊環境(module environment) 也是一個 詞法環境 ,它包含模塊頂級聲明的綁定。它還包含模塊顯式導入的綁定。模塊環境的外部環境是 全局環境(global environment)
  • 函數環境(function environment)函數環境(function environment) 也是一個 詞法環境 ,對應於 ECMAScript 函數對象的調用。函數環境(function environment) 能夠創建新的此綁定。函數環境還支持 super 調用所需的狀態。

執行上下文(Execution Contexts)

執行上下文(Execution Contexts)ECMAScript 代碼 運行時(runtime) 的上下文環境。微信

在同一時間內,每次只能有一個 執行上下文(Execution Contexts) 運行。這稱爲 運行執行上下文(running execution context)前端工程師

執行上下文堆棧(execution context stack) 用於跟蹤 執行上下文(Execution Contexts)函數

正在運行的 執行上下文(Execution Contexts) 始終是此堆棧的頂級元素。學習

每當控制從與當前運行的執行上下文相關聯的可執行代碼轉移到與該執行上下文無關的可執行代碼時,就建立新的執行上下文。ui

新建立的執行上下文被壓入堆棧併成爲正在運行的 執行上下文

全部執行上下文的狀態組件(State Components)

組件 做用
code evaluation state 用來判斷當前執行上下文的狀態
Function 判斷當前執行上下文狀態,若是當前上下文是函數,則上下文環境爲此函數對象,不然則爲null
Realm 相關代碼從中訪問 ECMAScript 代碼的領域(Realm)記錄。
ScriptOrModule 判斷當前代碼環境,若是沒有所處的腳本或模塊環境,就像在 InitializeHostDefinedRealm(原生方法) 中建立的原始執行上下文的狀況同樣,使上下文環境爲 null

做用域鏈(Scope chain)

做用域 負責收集和維護由全部聲明的標識符(變量)組成的一系列查詢,並實施一套很是嚴格的規則,肯定當前執行的代碼對這些標識符的訪問權限。-- 《你不知道的JavaScript(上卷)》

從上面兩個話題,咱們能夠知道,除 全局做用域(global scope) 外,每一個做用域始終鏈接到其背後的一個或多個做用域,從而造成 做用域鏈(scope chain)全局做用域(global scope) 沒有任何父級,這也是有意義的,由於它位於層次結構的頂部。

咱們看下面的代碼:

const bestAvenger = "Iron man";
function a() {
  const bestActor = "Neymar";
  console.log(bestAvenger); // output:Iron man
    
  function c() {
    const bestProgrammingLanguage = "Html";
    console.log(bestActor); // output:Neymar
    b();
  }
  c();
}
function b() {
  console.log(bestProgrammingLanguage); //**not defined error**
}
a();
複製代碼

上面的代碼會報錯以下:bestProgrammingLanguage is not defined.

如上所述,做用域鏈(Scope chain) 始終是 詞法 建立的。做用域 的父節點由 執行上下文(函數) 在代碼中的詞法或物理位置定義。

上面代碼的 做用域鏈(Scope chain) 以下:

詞法環境 做用域鏈
全局做用域下的G G = G
G下的A A = A + G
G下的B B = B + G
G下的A,A下的C C=C+A => C+A+G

其實總結起來就是:

  • 每當編譯器遇到變量或對象時,它都會遍歷當前執行上下文的整個 做用域鏈(Scope chain) ,若是沒有在那裏找到它,就遍歷 原型鏈(prototype chain),若是也沒有找到,它會拋出未定義的錯誤。
  • 編譯器經過查看函數在代碼中的位置來建立函數的做用域。
  • 編譯器建立 做用域鏈(Scope chain)全局做用域(Global Scope) 位於此層次結構的頂部。
  • 當代碼中使用變量時,編譯器會向後查看做用域鏈,若是找不到,則拋出未定義的錯誤。

參考資料

  1. ECMA文檔
  2. Javascript Scope Chain and Execution Context simplified


若是你、喜歡探討技術,或者對本文有任何的意見或建議,你能夠掃描下方二維碼,關注微信公衆號「 魚頭的Web海洋」,隨時與魚頭互動。歡迎!衷心但願能夠碰見你。

相關文章
相關標籤/搜索