前言
好的,接著我們要假裝自己是 JS 引擎,然後用 ECMAScript 文件上的規則來找出上一節開頭問題的答案哦~
hoisting & scope 小測驗
1 | var a = 1; |
請依序寫出 log 答案是多少,在這邊我們要採用不一樣的觀點來找出這題的答案。
如果我是 JavaScript 引擎
我們說過當 JavaScript 執行時,會先創造全域執行環境。
- 接著會找有沒有參數,但它不是函式所以跳過
- 再來會找有沒有宣告函式
- 最後才找有沒有宣告變數,有則初始化為
undefined
整理後可以得到這樣的結果
接下來開始逐行執行程式碼
- 1163 行 全域變數
a被賦值為 1 - 跳過 1164 ~ 1177 行的函式
- 1178 行呼叫
test函式,建立並進入另一個執行環境
test 函式內發生的事
基本上做的事情會跟創造全域執行環境時一樣,因此:
- 於 1172 行發現宣告
inner函式 - 於 1166 行發現宣告
a變數
整理後可得結果如下,至此 test 的執行環境建立完成。
接著逐行執行 test 函式內的程式碼
- 於 1165 行印出變數
a,此時對照test的 VO ,得知目前a為undefined - 於 1166 行變數
a被賦值為 7 ,此時test內 VO 的 a 為 7 - 於 1167 行印出變數
a,對照後得知目前a為 7 - 於 1168 行執行
a++, 此時test內 VO 的a為 8 - 於 1169 行發現
var a,但已經有同名變數被宣告,直接跳過。 - 於 1170 行呼叫
inner函式,建立並進入另一個執行環境
至此,狀態如下

inner 函式內發生的事
與前面介紹的一樣,會先創造執行環境,因此
- 在這個函式內找不到任何的參數、宣告函式、宣告變數
所以當前狀態是這樣的

接著逐行執行 inner 函式內的程式碼
- 於 1173 行印出變數
a,但所屬 VO 內找不到變數a,轉而向上一層尋找,此時會找到 test VO 的a,所以會印出 8 - 於 1174 行對變數
a賦值,但所屬 VO 內找不到變數a,所以這邊的賦值其實是對 test VO 的a,因此被重新賦值為 30 - 於 1175 行對變數
b賦值,但逐層往上找也找不到變數b,最後會在全域執行環境內產生變數b,並賦值 200
至此 inner 函式執行完畢,被移出執行堆。

目前執行堆最上方是 test 函式。
因此會回到 test 函式內繼續進行沒執行的部分
- 於 1171 行印出變數
a,此時對照a為 30 - 1172 ~ 1177 跳過
至此 test 函式執行完畢,被移出執行堆。

回到全域執行環境,繼續進行沒執行的部分。
- 於 1179 行印出變數
a, 此時對照a為 1 - 於 1180 行對變數
a賦值,因此a被修改為 70 - 於 1181 行印出變數
a, 此時對照a為 70 - 於 1182 行印出變數
b,此時對照b為 200
至此,程式碼執行完畢,全域執行環境被移出。

接著我們可以實際運行這一段程式碼,會發現答案是吻合的。

是不是相當的神奇呢?
心得
若要我說觀看影片到現在的心得,我覺得最屌的莫過於上一篇跟這一篇了,沒想到還可以用這樣子的方式了解 hoisting ,這是我上 JS 奇怪部份時完全沒有的體驗,真的是太棒了!
但我知道後面應該還有更多類似這樣的體驗,真的是很開心自己能有這樣的機會學習關於 JavaScript 底層。