2015年12月11日

[筆記] 不同execution context的變項不會互相影響─了解function背後運作的邏輯


在JavaScript中要執行一段函式(function)並不困難,只要在函式的最後加上括號 ( ) 就可以了,在英文中,我們把執行函式的執行這個動作稱做invoke。但是,我們需要進一步了解函式運行時背後的邏輯,了解背後運作的原理,我們才可以避免不必要的錯誤產生。

了解函式背後運作的邏輯


讓我們先來看一下這段程式碼:


這段程式碼背後運作的邏輯是這樣的:

首先,Global Execution Context(A wrapper to help manage the code that is running)會被建立,這時候會一併建立thisglobal object (window),在程式開始執行的過程中,function a 和 b 由於 hoisting 的緣故會先被建立在記憶體中,接著才會開始逐行執行程式。


接著,程式碼會執行到 a( ) 這個部分,這時候,會建立a的execution context,並且被放置到execution stack中。在這個execution stack中,最上面的execution context會是正在執行的。


function a 的execution context建立後,便會開始執行function a中的內容。由於在 function a( ) 裡面有去執行到 function b ,因此,在這個execution stack中,最上面會變成 function b 的execution context。


當 function b 執行完之後,會從execution stack中離開,繼續逐行執行function a。當 function a 執行完之後,一樣會從execution stack中抽離,在回到Global Execution Context逐行執行。



不同 execution context 中的變項不會互相影響


在了解了一般的程式其運作背後的邏輯後,讓我們來看一下這段程式碼:


你可以想像,如果我們在不同的execution context中去把myVar這個變項呼叫出來,會得到什麼結果呢?

答案如下:


我們分別得到了1、undefined和2。為什麼會這樣呢?

讓我們來看看這段程式背後運作的邏輯:

首先,Global Execution Context會被建立,由於hoisting的緣故,myVar、function a 和 b 都會被建立並儲存在記憶體中,接著便開始逐行執行程式。一開始會碰到 var myVar = 1 所以,最外層的myVar便被給值為1,接著執行到了 console.log(myVar) ,這是在global execution context執行的,於是得到了第一個1的結果:


然後執行到了 a ( ),於是建立了a的execution context,這時候由於逐行執行的關係,會先執行到 var myVar = 2 ,但因為這是在function a的execution context中,所以並不會影響到global execution context的myVar:


在執行完function a中的 var myVar = 2 後,繼續逐行執行,於是執行到了b ( ) ,這時候,function b的execution function便被建立,而且會像急件插件一樣先去執行function b裡面的內容:


function b的execution function建立後,會開始插件逐行執行function b裡面的內容,於是讀到了 var myVar;,這時候在function b這個execution context中的myVar變項被建立,但是還沒被給值,所以會是undefined。和上面提到的一樣,由於這個myVar是在function b的execution context中所建立,所以並不會影響到其他execution context的myVar,這時候執行到了function b execution context中的 console.log(myVar) ,於是得到了第二個看到的undefined:


最後,function b執行完之後,會從execution stack中離開,繼續回到function a中的 b( )後逐行執行,也就是 console.log(myVar) ,這時候是在function a的execution context加以執行的,因此也就得到了結果中看到的第三個2了。

最後由於b ( ) 後面已經沒有內容,function a執行完畢,這時候,function a也會從execution stack中抽離。


最後回到Global Execution Context,如果程式中的a( )後面來有內容的話,會繼續進行逐行執行。

由上面的例子,我們可以知道,我們是在不同的execution context中分別去宣告變項myVar,因此在不同的execution context,變項彼此之間不會影響,所以雖然這三個變項都叫做myVar,但其實是三個不同的變項。

由於我們是在不同的execution context中去宣告變項,所以這其實是位於三個不同execution context中的變項,所以即使我們是在執行完a( )後再去呼叫一次myVar,一樣會得到"1"的結果。


提醒


需要注意的是,如果是在function裡面直接使用myVar這個變項,而沒有透過var重新宣告它的話,就會得到不同的結果!因為在未宣告新的變項的情況下,在該execution context中JavaScript engine找不到這個變項,它就會往它的外層(outer lexical environment)去尋找,最後會得到,1,2,2,2的結果。

程式範例

function b(){
 var myVar;
 console.log(myVar);
}

function a(){
 var myVar = 2;
 b();
 console.log(myVar);
}

var myVar = 1;
console.log(myVar);
a();
→回到此系列文章目錄

學習來源

0 意見:

張貼留言