[JavaScriptWeird]No.7 函式呼叫與執行堆

前言

在前面我們已經知道了全域執行環境是如何被創造、執行的,在本篇我們要記錄的是「函式呼叫 ( Funtion Invocation)」、「執行堆 (Execution Stack)」究竟是什麼東西~這對之後的進階觀念相當重要!

函式呼叫 (Funtion Invocation)

表示執行或者呼叫一個函式,在 JavaScript 我們用括號來表示這件事,像是這樣:

1
a();

這麼做我就呼叫了 a 函式,儘管我根本沒宣告 a 函式,但我希望 JavaScript 執行這個。

執行堆 (Execution Stack)

這個就得從 「當我們在 JavaScript 呼叫函式時發生了什麼事」開始說起,一個簡單的範例:

1
2
3
4
5
6
7
function b(){  
}
function a(){
 b();
}

a();

狀況是這樣的:我們有兩個函式, a 函式會呼叫 b 函式。我們呼叫 a 函式,這時候來看會發生什麼事情。

  • 首先,當我們執行了 JavaScript 第一個被創造的是「全域執行環境」,語法解析器會分析程式,然後編譯器會編譯程式,這個過程也會創造變數 this ,創造全域物件 (在瀏覽器會是 window 物件),然後會將這些變數、函式放進記憶體中。接著程式碼會開始逐行被執行,直到碰到 a()
  • 碰到 a() 時,一個新的執行環境被創造,被放進執行堆中,就像積木一樣被堆疊起來,在最上方的就是正在執行的東西。所以每一次在 JavaScript 呼叫函式,就會創造一個新的執行環境並放進執行堆中。然而一個新的執行環境會有自己的記憶體空間存放變數與函式,一樣也會歷經創造階段,逐行執行函式中的程式碼。
  • 接著執行 a 函式內的程式碼呼叫了 b 函式,此時會停止執行程式再次創造另一個新的執行環境,然後執行 b 函式,儘管 b 函式內沒有任何程式碼。
  • b 函式結束後,它會離開執行堆的最上方,然後是函式 a ,但在此函式 a 內沒有其他的程式碼了,也離開了執行堆,最後回到最下面的全域執行環境。

克服 JS 奇怪部分 截圖

程式碼中的實際排列順序並不重要,在函式中剩下的程式碼順序也不會影響執行先後,像是把程式碼改成這樣:

1
2
3
4
5
6
7
8
9
function a(){  
b();
var c;
}
function b(){
var d;
}
a();
var d;

如前面所提,這些函式、變數在創造階段就已經在記憶體中了,因此當程式碼逐行被編譯時,也很類似上個範例:

  • a 函式被呼叫,進入執行堆,變成目前執行的程式,因為 var da 函式的下方且 JavaScript 是同步的,一次執行一行,所以暫時沒被執行。
  • 逐行執行 a 函式的內容, b 函式被呼叫,進入執行堆,變成目前執行的程式,注意這時 a 函式並沒有離開執行堆。
  • 接著執行 b 函式的內容,當執行完之後 b 函式就離開執行堆,此時 a 函式又變成執行堆的最上面了,因此會執行還沒執行的那一行 var c
  • a 函式 執行完畢離開執行堆,回到全域執行環境,還記得一開始的 var d 嗎 ? 終於輪到它了!

後記:

透過一個比較簡單與進階的例子,我們了解 JavaScript 函式呼叫的流程,即使是自己呼叫自己,仍然會遵守這個流程。此外,無論在執行堆最上面的東西是什麼,那就是目前正在執行的程式。

0%