一文詳解JavaScript中執行上下文與執行棧(圖文結合)

2022-10-13 22:00:33

前端(vue)入門到精通課程:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:


對於我們前端開發者來說理解JS程式內部執行機制是必要的,其中一個關鍵概念就是Js的執行上下文和執行棧。執行上下文是JS語言較為底層的知識,學習掌握有助於我們更深入的把握JS這門語言的本質,也有助於理解作用域、閉包、變數提升等相關知識。

1 何為執行上下文

程式碼執行前,瀏覽器的Js引擎先會建立程式碼執行的環境來處理此Js程式碼的轉換和執行,程式碼的執行環境稱為執行上下文。

執行上下文是一個抽象概念,包含當前正在執行的程式碼以及幫助其執行的所有內容。

2 分類

執行上下文主要分為三類:

  • 全域性執行上下文 —— 全域性程式碼所處的環境,不在函數內部程式碼都在全域性執行
  • 函數執行上下文 —— 在函數呼叫時建立的上下文。
  • Eval執行上下文 —— 執行在Eval函數中程式碼時建立的環境,Eval由於效能問題在我們平時開發中很少用到,所有這裡我們不在討論。

接下來我們重點來講全域性上下文和函數上下文。

2.1 全域性執行上下文

當我們的JS檔案跑起來之後,首先建立的就是全域性執行上下文。

當我們的檔案裡沒有一行程式碼時,全域性執行上下文中比較乾淨,只有兩個東西。

  • 全域性物件(瀏覽器裡是WindowNode環境下是Global
  • this變數(指向的還是全域性物件)

這時候如果我們在檔案裡寫點東西,比如我寫如下程式碼:

var name = '小明'
var age = 18
function showName(){
  return {
      name : name,
      age : age
  }
}
登入後複製

全域性執行上下文就會立刻變成這個樣子:

1.png

上圖可以看到,我們明明給nameage賦值了,咋還會顯示undefined呢?這是因為執行上下文分為兩部分,建立階段和執行階段。

  • 建立階段 —— 執行上下文的初始化狀態,做一些準備工作
  • 執行階段 —— 程式碼一行一行執行

以上就是建立階段的全域性上下文概況,在建立階段JS引擎將會做以下幾件事:

  • window作為全域性執行上下文物件
  • 建立this,this指向window
  • 給變數和函數安排記憶體空間
  • 變數賦值undefined,函數宣告放入記憶體
  • 放入作用域鏈

接下來才會進入了全域性執行上下文的執行階段,也就是賦值階段,如下圖:

2.png

需要注意的是執行上下文執行階段是一行一行執行的,如下圖所示:

3.png

2.2 函數執行上下文

理解完全域性執行上下文,函數執行上下文也我們只需要關注它與全域性上下文之間的不同即可,兩者之間不同主要表現在以下三個方面:

  • 建立時機:全域性執行上下文在執行JS檔案之前就被建立,而函數執行上下文則是在函數呼叫時建立。
  • 建立頻率:全域性上下文在程式碼剛開始被執行前建立一次,而函數執行上下文由指令碼裡函數呼叫次數決定,可以建立無數次。
  • 建立內容:全域性執行上下文將window作為全域性物件,而是函數執行上下文則是建立引數物件arguments;建立的this也不會指向全域性物件,而是取決於函數是如何呼叫的。

我們通過下面這個例子來看函數上下文不同階段的表現:

var name = '小明'
var age = 18
function showName(){
  return {
      name : name,
      age : age
  }
}

// 呼叫該函數
showName()
登入後複製

當我們呼叫showName函數時,就會進入到函數執行上下文的建立階段,函數執行上下文的場景如下:

4.png

接著就會進入到執行階段,這個階段函數內程式碼才會一行一行執行,這個例子中,因為沒有涉及到變數的修改,因此函數上下文的內容保持不變,執行完畢後,函數上下文的生命週期就結束了。

當我們呼叫showName函數時,在瀏覽器中執行狀況:

5.png

3 執行棧

我們看到當函數執行完後,其對應的執行上下文也隨之消失了。這個消失的過程,我們叫它出棧——在JS程式碼執行過程中,JS引擎會為我們建立「執行上下文棧」。

在全域性程式碼執行前,JS引擎為了管理執行上下文,確保程式的執行順序。JS引擎會建立一個棧來管理所有的所有的執行上下文物件

因為函數上下文可能會存在多個,我們不可能保留所有的上下文。當一個函數執行完畢,其對應的上下文必須讓出之前所佔用的資源。因此上下文的建立和銷燬,就對應了一個」 入棧 「和」 出棧 「的操作。

當我們呼叫一個函數的時候,就會把它的上下文推入呼叫棧裡,執行完畢後出棧,隨後再為新的函數進行入棧操作。

我們通過一個例子來看一下這個過程:

function testA(){
  console.log('執行第一個測試函數的邏輯');
  testB();
  console.log('再次執行第一個測試函數的邏輯');
}

function testB(){
  console.log('執行第二個測試函數的邏輯');
}

testA()
登入後複製

1、執行之前,全域性上下文建立:

6.png

2、testA呼叫,testA對應函數上下文建立:

7.png

3、testB呼叫,testB對應函數上下文建立:

8.png

4、testB執行完畢,對應上下文出棧,剩下testA和全域性執行上下文:

9.png

5、testA執行完畢,對應執行上下文出棧,此時只剩下全域性上下文:

10.png

在這整個過程裡,呼叫棧的變化示意如下:

11.png

4 總結

  • 執行上下文是什麼:程式碼執行前,Js引擎會建立程式碼的執行環境,程式碼的執行環境稱作執行上下文,包含當前正在執行的程式碼以及幫助其執行的所有內容。

  • 全域性執行上下文:(1)將window作為全域性執行上下文物件(2)建立this,this 指向window(3)給變數和函數安排記憶體空間(4)變數賦值undefined,函數宣告放入記憶體(5)放入作用域鏈

  • 全域性與函數執行上下文不同:(1)全域性:在檔案執行前建立;函數:在函數呼叫時建立(2)全域性:只建立一次;函數:呼叫幾次建立幾次(3)將window作為全域性物件;函數:建立引數物件arguments,this指向呼叫者

  • 執行棧:管理所有的執行上下文物件

【相關推薦:、】

以上就是一文詳解JavaScript中執行上下文與執行棧(圖文結合)的詳細內容,更多請關注TW511.COM其它相關文章!