[JavaScriptWeird]No.60 cosplay JS 引擎

前言

好的,接著我們要假裝自己是 JS 引擎,然後用 ECMAScript 文件上的規則來找出上一節開頭問題的答案哦~

hoisting & scope 小測驗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var a = 1;  
function test(){
console.log('1.', a);
var a = 7;
console.log('2.', a);
a++;
var a;
inner();
console.log('4.', a);
function inner(){
console.log('3.', a);
a = 30;
b = 200;
}
}
test();
console.log('5.', a);
a = 70;
console.log('6.', a);
console.log('7.', b);

請依序寫出 log 答案是多少,在這邊我們要採用不一樣的觀點來找出這題的答案。

如果我是 JavaScript 引擎

我們說過當 JavaScript 執行時,會先創造全域執行環境

  • 接著會找有沒有參數,但它不是函式所以跳過
  • 再來會找有沒有宣告函式
  • 最後才找有沒有宣告變數,有則初始化為 undefined

整理後可以得到這樣的結果

接下來開始逐行執行程式碼

  • 1163 行 全域變數 a 被賦值為 1
  • 跳過 1164 ~ 1177 行的函式
  • 1178 行呼叫 test 函式,建立並進入另一個執行環境

test 函式內發生的事

基本上做的事情會跟創造全域執行環境時一樣,因此:

  • 於 1172 行發現宣告 inner 函式
  • 於 1166 行發現宣告 a 變數

整理後可得結果如下,至此 test 的執行環境建立完成。

接著逐行執行 test 函式內的程式碼

  • 於 1165 行印出變數 a ,此時對照 test 的 VO ,得知目前 aundefined
  • 於 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 底層。

0%