hw3:Hoisting
請說明以下程式碼會輸出什麼,以及盡可能詳細地解釋原因。
var a = 1
function fn(){
console.log(a)
var a = 5
console.log(a)
a++
var a
fn2()
console.log(a)
function fn2(){
console.log(a)
a = 20
b = 100
}
}
fn()
console.log(a)
a = 10
console.log(a)
console.log(b)
Answer :

Step1
開始編譯程式 => globalEC 產生並初始化 => 正式執行程式 => global 的 a 賦值為 1 => 遇到 fn() => fnEC 初始化
要特別注意在 fnEC初始化的時候,會遇到 var a =5 ,此時即時 global 已經有 a 了,還是會宣告 a 一次,並且初始化成 undefined,
如果程式改成這樣,
var a = 1
function fn(){
console.log(a)
}
那麼 fnEC 就不會再宣告一次,且會印出 1。
那麼因為 fn 裡面有再宣告一次 var a =5,所以再第二次遇到 var a 的時候,會忽略它,因為已經宣告過了。
這是因為 var 允許重複宣告的特性,如果是改用 let 的話,程式就會報錯!
此時 globalEC 與 fnEC 的 scopeChain 如下:
fnEC.scopeChain = [fnEC.AO, fn.[[Scope]] = [fnEC.AO, [globalEC.VO]]
fn2.[[Scope]] = fnEC.scopeChain = [fnEC.AO, fn.[[Scope]]] = [fnEC.AO, [globalEC.VO]]
Step2
執行 fn => console.log(a) //undefined => fn 裡面 的 a 賦值為 5 => console.log(a) // 5 => 遇到 a++ fn 裡面的 a 變成 6 => var a 因為已經宣告過了,當作沒看到 => 遇到 fn2() => fn2的 EC 初始化開始

進入 fn2 初始化 => 沒有遇到變數與 function 的宣告 => 所以初始化階段只有建立 scopeChain => (fn2EC.scopeChain 詳細描述在圖示中)

開始執行 fn2 => console.log(a) // 6 => 遇到 a = 20 ,將 fn 的 a 改成 20 => 遇到 b=100 ,因為在 scopeChain 都找不到 => 在非嚴格模式下會在 global 建立 b 且 b =100 ,如果是嚴格模式下會報錯。

fn2 執行完畢 => 回到 fn 繼續執行 => console.log(a) //20 => fn 執行完畢 => 回到 global => console.log(a) //1 因為目前已經是在 global 中,所以會從 globalEC.scopeChain 找 a ,這時候的 a 為 1 => 再來又將 a 重新賦值為 10 => globalEC.VO 裡面的 a 從 1 變成 10 => console.log(a) // 10 => console.log(b) //100 剛剛在 fn2 時有遇到 b=100 時就已經幫我們在 globalEC 加上 b=100 了,所以 b = 100。
將以上的步驟拼湊起來,我們可以得到這個程式跑出來的結果:
5
6
20
1
10
100


