JavaScript Weird Part (8) - 函數呼叫與執行堆

全域執行環境的創造與執行與和函式脫不了關係,函式呼叫與執行堆更是其中兩個重要環節。

在 Javascript 裡,只要是「函數名稱+()」就是執行或呼叫這個函數時,稱作 “invoke the function” 或 “function invocation”。但是 JavaScript 呼叫函數時發生什麼事情?

呼叫函式

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

function a() {
b();
}

a();

因為會先創造全域執行環境,語法解析器會分析程式,接著編譯器會編譯程式,然後創造全域執行環境,也會創造變數 this 與全域物件;在瀏覽器則會創造 window 物件。接著將這些函式放進記憶體中。

在最先的創造階段,ba 都會被放到記憶體裡,接著程式碼會逐行被執行。最開始當他們還沒被呼叫的時候,不會執行函式裡的程式。當它遇到最下面的a();,它會呼叫 a 函式。

因此,當創造一個執行環境,它就被放進執行堆 (execution stack) 裡面(所謂「堆」正如其名,是一個一個疊在一起),誰在最上面就是正在執行的東西。所以每一次 Javascript 呼叫函數,就會創造一個新的執行環境,然後將它放到執行堆裡面。因此創造一個新的執行環境,就像全域環境一樣,會有自己的記憶體空間給函數和變數,會有創造階段,然後逐行執行函式裡面的程式碼。

執行堆

然而,當我們呼叫另一個函式,它會停止執行程式,創造另一個執行環境,接著執行。

即便在函式 b 沒有任何內容程式要執行,但這就是 JavaScript 在呼叫函數時會依序完成的工作。當函式 b 結束以後,因為它在執行堆最上面,它會離開執行堆,接著是 函式 a 離開執行堆,最後回到底下的全域執行環境。

程式中排列的順序並不影響執行的結果,也不會影響執行先後。雖然 a()貌似在 b()前面,可是這些函式在創造階段裡就已經在記憶體裡面,在全域執行環境被初始化的時候就在了;函數下面的var c、 var d 也在記憶體裡面。

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

function b() {
var d;
}

a();
var d;

首先 a 函式會被呼叫, a 的執行環境會進入執行堆,接著他會變成目前正在執行的程式,最後一行的 var d 不會被呼叫,因為 JavaScript 是同步的,一次執行一行,現在執行的程式就是現在的執行環境,也就是執行堆最上面的那個。所以逐行執行 a 函式的時候,就會呼叫 b 函式。b 創造自己的執行環境,然後進入執行堆最上面,開始逐行執行,執行完後就要回去執行 a 函數。為什麼?因為當我們結束那個函數後,執行函數就會離開執行堆(popped off of the stack)。

現在最上面的是 a,現在執行剛剛在執行環境還沒執行的那行 var c,當 a 執行完離開執行堆,下一行還沒被執行的程式,在全域執行環境裡也就是 ``var d。

所以 JavaScript 的程式碼運作,在每一次函數被呼叫,就會為函數創造一個新的執行環境,this 變數被創造給那個函數。每當函數被呼叫,即使是被自己呼叫,一個新的執行環境就會進入執行堆,執行完畢,離開執行堆。無論上面是什麼,它就是逐行、同步正在執行的程式。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2020 CYC'S BLOG All Rights Reserved.

UV : | PV :