1. 程式人生 > >javascript 通過執行上下文來跟蹤程式碼

javascript 通過執行上下文來跟蹤程式碼

在javascript中,程式碼執行的基礎單元是函式。我們時刻使用函式,使用函式進行計算,使用函式更新UI,使用函式達到複用程式碼的目的,使用函式讓我們的程式碼更容易理解。為了達到這個目標,第一個函式可以嗲用第二個函式,第二個函式可以呼叫第三個函式,以此類推。當發生函式呼叫時,程式會回到函式呼叫的位置。

 

JavaScript引擎是如何跟蹤函式的執行並回到函式的位置的?

JavaScript程式碼有兩種型別:一種是全域性程式碼,在所有函式外部定義;

一種是函式程式碼,位於函式內部。JavaScript引擎執行程式碼時,每一條語句都處於特定的執行上下文中。

 

既然具有兩種型別的程式碼,那麼就有兩種執行上下文:全域性執行上下文和函式執行上下文。

二者最重要的差別時:全域性執行上下文只有一個,當JavaScript程式開始執行時就已經建立了全域性上下文;

而函式執行上下文是在每次呼叫函式時,就會建立一個新的。

 

注意:當呼叫函式時可通過關鍵字訪問函式上下文。函式執行上下文,雖然也稱為上下文,但完全是不一樣的概念。執行上下文是內部JavaScript概念,JavaScript引擎使用執行上下文來跟蹤函式的執行。

 

JavaScript基於單執行緒的執行模型:在某個特定的時刻只能執行特定的程式碼。一旦發生函式呼叫,當前的執行上下文必須停止執行,並建立新的函式執行上下文來執行函式。當函式執行完成後,將函式執行上下文銷燬,並重新回到發生呼叫時的執行上下文中。所以需要跟蹤執行上下文——正在執行的上下文以及正在等待的上下文。最簡單的跟蹤方法是使用執行上下文棧(或稱為呼叫棧)。

 

注意:棧是一種基本的資料結構,只能在棧的頂端對資料項進行插入和讀取。這種特性可類比於自助餐廳裡的一疊托盤,你只能從托盤堆頂端拿到一個托盤,服務員也只能將新的托盤放在這疊托盤的頂端。

console.log("-------------------建立執行上下文-----------------------");
//一個函式呼叫另一個函式
function skulk(ninjaTest) {
    report(ninjaTest + " skulking!");
}
//通過內建的console.log方法傳送訊息
function report(message) {
    console.log(message);
}

//在全域性中分別呼叫兩個函式
skulk("Kuma");
skulk("Yoshi");

 

從上圖中看出:執行上下文的行為如下:

1.每個JavaScript程式只建立一個全域性執行上下文,並從全域性執行上下文開始執行(在單頁應用中每個頁面只有 一個全域性執行上下文)。當執行全域性程式碼時,全域性執行上下文處於活躍狀態。

2.首先在全域性程式碼中定義兩個函式:skulk和report,然後呼叫skulk("Kuma")。由於在同一個特定的時刻只能執行特定程式碼,所以JavaScript引擎停止執行全域性程式碼,開始執行帶有Kuma引數的skulk函式。建立新的函式執行上下文,並置入執行上下文棧的頂部。

3.skulk函式進而呼叫report函式。又一次因為在同一個特定時刻只能執行特定程式碼。所以,暫停skulk執行上下文,建立新的Kuma作為引數的report函式的執行上下文,並置入執行上下文棧的頂部。

4.report通過內建函式console.log打印出訊息後,report函式執行完成後,程式碼又回到了skulk函式。report執行上下文從執行上下文棧頂部彈出,skulk函式執行上下文重新啟用,skulk函式繼續執行。

5.skulk函式執行完成後也發生類似的事情:skulk函式執行上下文從棧頂端彈出,重新啟用一直在等待的全域性執行上下文並恢復執行。JavaScript的全域性程式碼恢復執行。

skulk函式第二次執行時,整個過程也是類似的,只是引數變成了Yoshi。分別建立新的函式執行上下文skulk("Yoshi")和report("Yoshi skulking"),並依次置入執行上下文棧的頂部。

每個函式執行完成時,對應的函式上下文從執行上下文棧頂部彈出。

雖然執行上下文棧(execution context stack)是JavaScript內部概念,但仍然可以通過JavaScript調式器中檢視,在JavaScript偵錯程式中可以看到對應的呼叫棧(call stack)。

 

 

 

執行上下文除了可以跟蹤應用程式的執行位置之外,對於識別符號也是至關重要,在靜態環境中通過執行上下文可準確定位識別符號實際指向的變數。

 

參考《JavaScript忍者祕籍》