JS入門難點解析4-執行上下文棧

weixin_33850890發表於2018-01-28

(注1:如果有問題歡迎留言探討,一起學習!轉載請註明出處,喜歡可以點個贊哦!)
(注2:更多內容請檢視我的目錄。)

1. 簡介

在本系列的第二篇文章JS入門難點解析2-JS的變數提升和函式提升中,我們已經討論過。之所以不說JS需要編譯,只是它不像其他編譯語言一樣需要翻譯成等價的另一種語言。但是仍然需要進行語法分析和程式碼生成,並且通常是立即執行。而且,JS的變數提升和函式提升就發生在編譯階段。

回顧一下:

var foo = function () {
    console.log('foo1');
}
foo();  // foo1
var foo = function () {
    console.log('foo2');
}
foo(); // foo2

以及

function foo() {
    console.log('foo1');
}
foo();  // foo2
function foo() {
    console.log('foo2');
}
foo(); // foo2

這兩段程式碼,前一段進行了變數宣告提升,後一段進行了函式宣告提升。我們講到過,這是因為 JavaScript 編譯器和引擎並非一行一行地分析和執行程式,而是一段一段地分析執行。當分析執行一段程式碼的時候,會進行一個“準備工作”,包括變數宣告提升和函式宣告提升等。那麼這裡所謂的一段指的是什麼呢?到底JavaScript編譯器和引擎遇到一段怎樣的程式碼時才會做“準備工作”呢?這就需要了解什麼是可執行程式碼了。

2. 可執行程式碼

JavaScript 的可執行程式碼(executable code)有以下三類:全域性程式碼、函式程式碼、eval程式碼

當JS引擎遇到這三類程式碼時,會開始做準備工作,建立一個“執行上下文(execution context)"。

舉例說明,當JS執行到一個函式的時候,就會建立該函式的“執行上下文(execution context)"。那麼問題來了,JS程式碼中可能出現為數眾多的函式,如何管理建立的那麼多執行上下文呢?

3. 執行上下文棧

JavaScript 引擎建立了執行上下文棧(Execution context stack,ECS)來管理執行上下文。

為了模擬執行上下文棧的行為,讓我們定義執行上下文棧是一個陣列:

ECStack = [];

試想當 JavaScript 開始要解釋執行程式碼的時候,最先遇到的就是全域性程式碼,所以初始化的時候首先就會向執行上下文棧壓入一個全域性執行上下文,我們用 globalContext 表示它,並且只有當整個應用程式結束的時候,ECStack 才會被清空,所以 ECStack 最底部永遠有個 globalContext。

ECStack = [
    globalContext
];

現在 JavaScript 遇到下面的這段程式碼了:

function fun3() {
    console.log('fun3')
}
function fun2() {
    fun3();
}
function fun1() {
    fun2();
}
fun1();

執行一個函式的時候,就會建立一個執行上下文,並且壓入執行上下文棧,當函式執行完畢的時候,就會將函式的執行上下文從棧中彈出。知道了這樣的工作原理,讓我們來看看如何處理上面這段程式碼:

// 虛擬碼

// fun1()
ECStack.push(<fun1> functionContext);

// fun1中呼叫了fun2,還要建立fun2的執行上下文
ECStack.push(<fun2> functionContext);

// fun2還呼叫了fun3
ECStack.push(<fun3> functionContext);

// fun3執行完畢
ECStack.pop();

// fun2執行完畢
ECStack.pop();

// fun1執行完畢
ECStack.pop();

// javascript接著執行下面的程式碼,但是ECStack底層永遠有個globalContext

參考

JavaScript深入之執行上下文棧
JS程式碼執行機制

相關文章