這個章節我們會直接介紹幾個專有名詞,包括前一章節提到的執行環境,加上執行堆疊,如果想要了解後面的提升、範疇等觀念,這些概念都是必要的,在後面的章節也會不斷被提到。
Outline
- 執行環境
- 執行堆疊
執行環境 ( Execution Context )
在前面提到直譯語言必須依賴環境才能被執行,在 JavaScript 裡面,提供這個環境的工作就是由 JavaScript 引擎來擔任。所以當我們說「瀏覽器執行/讀了你的 JavaScript 程式碼之後出現了錯誤」,其實並不真的是瀏覽器去讀你的程式碼,而是身為瀏覽器一部分的 JavaScript 引擎在做這件事。上面提到,能夠讓程式碼被執行的環境也被稱為「執行環境( Excution Context )」。
執行環境是一個抽象的概念,概括地來說,任何你JS 程式碼被執行、讀取的地方,像是 function 裡、甚至全域 ,都可以是執行環境。執行環境可以分為以下幾種:
- 全域執行環境 ( Global Ex. Context ) : JavaScript 預設的執行環境,不在任何函式裡面的程式碼就是在全域執行環境內,這個執行環境會做幾件事情:
- 創造全域環境(全域物件),(在瀏覽器裡面是 window ,在 Node.js 內是 global )
- 創造 this 物件,並將其指向這個全域物件,你可以打開瀏覽器的 console 並把 this 的值印出來試試看,關於 this 的指向在後面會提到。
- 記憶體指派流程(後面會提到)
-
函式執行環境( Functional Ex. Context ): 每當一個函式被呼叫,一個全新的執行環境也會跟著被創造出來,這也代表 JS 已經開始解析你的程式碼並執行。函式執行環境產生時做的事情差不多,差別是不會產生全域物件,而相對的會在函式內產生 argument 物件,內容是執行階段時函式引數的內容。每個函式都有屬於自己的執行環境,但是只會在函式被呼叫的時候才會產生,這種執行環境可以同時存在好幾個。
-
eval 函式內的執行環境:在 eval 函式內可以透過字串的方式去執行 JavaScript Code ,但是因為 eval 函式現在已經不常被使用,普遍也不被推薦使用,這邊就先不細提,想知道為什麼不被推薦,可以搜尋關鍵字「Eval is evil.」。
執行堆疊 ( Execution Stack )
上面我們提到,每當函式被呼叫時,就會產生對應的執行環境,而當函式裡有另一個函式被呼叫時,執行環境是按照什麼順序被產生的? JavaScript 使用後進先出的「堆疊」結構,依序來儲存隨著函式宣告所產生的執行環境。
各位應該都看過全面啟動吧?主角們為了完成任務不斷深入更下一層的夢境,而在每一層都有必須達成的目標,之後才能夠返回上一層,否則任務就會失敗。今天提到的堆疊其實是類似的概念,我們把「進入夢境」的動作對應到「函式呼叫」。
而到了下一層新的夢境,則呼應「產生執行環境」。你可以在裡面做任何你想做的事情,做完之後 return 回到上一層函式。放心,除非你喝醉了,否則寫 JS 是不會讓你進入混沌狀態的。讓我用一段程式碼來舉例,看看執行環境是如何被產生並且堆疊的:
let movie = 'Inception'
function firstLayerDream(){
return secondLayerDream()
}
function secondLayerDream(){
return thirdLayerDream()
}
function thirdLayerDream(){
return 'Mission Complete.'
}
let result = firstLayerDream()
- 當第一個位在全域的函式 firstLayerDream 被呼叫的時候,專屬的執行環境就產生了。
- 並且裡面的程式碼馬上就被「執行」(也就是開始被解析),直到碰到 return 關鍵字才會結束並離開這個執行環境。
- 隨著裡面的函式呼叫,第二層、第三層,的執行環境也被創造在裡面,一樣要等到 return ,才會結束並離開。
於是就有了這樣子的先後關係順序:
「堆疊」本身其實是一種資料結構,在堆疊裡面,某個元素之上如果還有有其他元素就無法被取出,因此有了「先進後出」的特性。如果你有吃過罐裝的品客應該可以很輕易知道我在說什麼,想想看,你沒辦法直接吃最底部的洋芋片對吧!
總結
今天稍微介紹了幾個非常重要的專有名詞,目前為止都還沒有進入 JS 語法的範疇,但這些觀念對於接下來的內容理解至關重要,請讀者一定要確定了解再往下,別擔心,如果有不理解的,隨時可以透過無線電聯絡我。