前言
好的,接著我們要假裝自己是 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 底層。