JS執行上下文

林晨熙發表於2019-03-31

執行上下文,即context,也不知道是誰翻譯的,不少的文獻、書籍用的都是這個詞。還記得第一次接觸這個詞時的惆悵、迷惘、不知所措,扶了扶眼鏡,翻開大辭典,還是翻譯成環境比較接地氣。JS執行上下文,即JS的執行環境。

執行環境(Execution Context, EC)

當我們的程式碼執行時,會進入到不同的執行上下文,即不同的環境。在不同的環境中,有著不同的 scope(作用域),程式碼所能訪問到的資源也就不同。在 JS 中,執行環境有如下三種情況:

  • 全域性環境 程式碼預設執行的環境,程式碼執行時會首先進入全域性環境。它是最外圍的一個執行環境,根據 ECMAScript 實現所在的宿主環境的不同,表示全域性環境的物件也不一樣。在 web 瀏覽器中,全域性環境就是 window 物件。全域性變數和函式都是作為全域性物件 window 的變數和方法來建立的。
  • 函式環境 函式被呼叫執行時,所建立的執行環境。
  • eval 不推薦使用,可忽略。

某個執行環境中的所有程式碼執行完畢後,該環境被銷燬,儲存在環境中的變數和函式也隨之銷燬。這些變數和函式儲存在一個叫做變數物件的物件中,關於變數物件將在變數物件與作用域鏈一文作詳細探討。

執行環境棧(Execution Context Stack, ECS)

瀏覽器中的直譯器被實現為單執行緒,同一時間只能處理一個任務,JS 程式中多個執行環境會以棧的方式來處理,這個棧叫做執行棧。棧底永遠都是全域性環境(視窗關閉時彈出),棧頂就是當前正在執行的環境。前述三種情況都會建立執行環境,執行環境建立時會被壓入棧頂,位於棧頂的環境執行完畢後就從棧頂彈出,並將環境控制權交給當前的棧。ECMAScript 程式中的執行流正是由這個方便的機制控制著。

來看下面的例子:

var firstName = 'snow';

function getName() {
    var lastName = 'John';

    function fullName() {
      var name = lastName + firstName;
      return name;
    }
    var name = fullName();
    return name;
}

getName();
複製程式碼

其執行棧變化過程如下圖所示:

  • 首先,將全域性環境壓入棧,開始執行程式碼,
  • 直到遇到getName(),準備呼叫函式,建立函式 getName 的執行環境,將其壓入棧頂並開始執行函式
  • 直到遇到fullName(),準備呼叫函式,建立函式 fullName 的執行環境,將其壓入棧頂並開始執行函式
  • 函式 fullName 執行時沒有再生成執行環境,執行完畢後則從棧頂彈出
  • fullName 執行棧彈出後,控制權回到了 getName 的執行棧,繼續執行程式碼,執行完畢,從棧頂彈出
  • 最後回到了全域性環境,視窗關閉後彈出 執行棧示意圖

結論

  • 單執行緒,同步執行,只有棧頂的環境處於執行中,其餘環境需要等待
  • 執行 JS 程式,首先進入全域性環境,全域性環境只有一個並在關閉視窗時彈出
  • 函式呼叫時會建立新的執行環境,包括呼叫自己

相關文章