[JS奇怪的世界]No.8 函數呼叫與執行堆

invocation (函數呼叫)

invocation 又稱函數呼叫,這是一個非常重要的觀念,所以當人家講 invocation function or function invocation,其實就是在講執行這個函數,而我們是透過括號()來執行函數。

課程有提供了一個基礎範例,幫助我們理解觀念 ↓

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

a();

這時候我們可以思考一下 JavaScript 是如何運作的。

  1. 首先全域執行環境會被先建立,也就是 window
  2. 語法解析器開始分析我們的程式內容 f-u-c-t-….(逐字解析)
  3. 編譯器開始將我們所撰寫的程式轉換電腦可以看懂得指令
  4. 開始創造「全域執行環境」,這時候會建立 windowthis 等全域物件
  5. 再來就進入創造階段 (Hoisting) 開始創造我們的變數及函數

那函數都會優先被儲存在記憶體中,直到我們呼叫這些函數,它才會執行函數中的程式,所以在前面的範例 ↓

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

這些時候函數只是儲存在記憶體中而已,直到它往下遇到我們所撰寫的 invocation,也就是 a() 時,才會開始執行函數並執行裡面的程式碼,那這邊稍微停止一下,當執行 a() 時候,其實產生了一個很奇妙的事情,新的執行環境被創造了,而這過程就是所謂的執行堆。

執行堆

而執行堆就是一層一層疊上去的,那誰在最上面誰就是正在執行中的東西,所以我們每次在呼叫函數時,都是在建立新的執行環境,然後放進執行堆中。

而這個新的執行環境很特別,它有屬於自己的全域執行環境、記憶體以及函數等,而它也會歷經創造、提升等階段,然後逐行逐字的執行程式碼。

所以當執行 b() 的時候 a() 就會停止運作,然後創造新的執行環境,所以由此可知每一個函數都會建立一個新的執行環境並且歷經創造等等階段,而程式碼的執行順序也不會受到執行堆的問題而影響。

但若如果這樣子呢?

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

a();
var d;

首先我們會知道 a() 函數會被呼叫而 a() 就會變成目前正在執行中的程式。

正在執行中的程式

但是首先最後一行的 var da() 的最後一行,因為函數 a() 還沒有執行完畢,所以不會被建立,主要原因是 JavaScript 是同步執行,並一次執行一行一個指令,所以它必須等函數 a() 執行完畢才會建立。

接下來 a() 函數執行過程中,它會呼叫 b(),然後 b() 就會開始建立自己的執行環境並且成為最上面的執行中的程式,然後在執行 var d,當 b() 執行完畢後 b 就會釋放掉(也就會離開執行堆),這時候就會只剩下 a() 還在執行堆中,因為它還要創造變數 var c,那它建立完畢之後呢?也是一樣會被釋放掉,這時候才會執行最後一行的 var d;

所以我們可以了解到每執行一個函數就會建立一個新的執行環境,然後逐行一行逐句逐字的同步執行,直到它執行完畢。

圖源

JavaScript 全攻略:克服 JS 奇怪的部分

0%