分別呼叫這些例子,會產生甚麼結果?
E.g. 1. : console.log 結果會是?
| 1 | console.log(a) | 
E.g. 2 : console.log 結果會是?
| 1 | console.log(a) | 
E.g. 3 : fnA() 是否執行? 又會產生甚麼?
| 1 | fn() | 
E.g. 4 : console.log 結果會是?
| 1 | console.log(fn) | 
變數提升
當我們宣告變數時,JS就會先開一個記憶體位置來存放,而它的預設值為 ‘undefined’,最後賦予我們給這個變數的值。
而 JS的執行順序為由上至下且依序執行,所以範例1與範例2的樣子就會是 :
E.g. 1 : console.log 結果會是 a is not defined
| 1 | console.log(a) | 
E.g. 2 : console.log 結果會是 undefined
| 1 | console.log(a) | 
實際上它的執行順序是這樣 :
| 1 | var a | 
函式提升
在 JS中,它會依據函式定義的位置順序,依序將函式提升到最上方,但在函式內的不會移動到外層。
E.g. 3 : fnA() 會執行並 return 1
| 1 | fn() | 
實際上它的執行順序是這樣 :
| 1 | var fn = () => 1 | 
而在函式內的函式不會提升到外層,但是可以呼叫外層的函式 :
| 1 | var fnA = () => { | 
不過,如果將匿名函式賦予到一個變數上,就會根據變數的規則走。
變數與函式提升的優先程度
在 JS中,會將優先將函式提升到最上方,接著再宣告變數,而函式的參數傳遞限於函式內並且是最優先,重新宣告它無任何意義。
E.g. 4 : console.log 結果會是 function fn( ){}
| 1 | console.log(fn) | 
| 1 | function fnA(a){ | 
Hosting 做了甚麼事
我們打開編輯器撰寫 JS時,其實是在一個全域環境底下開始寫程式,它同時是全域也是執行環境,當我們宣告一個 function時,也會同時建立 :
- 獨立的執行環境 ( Execution Contexts )
- 獨立的變數物件 ( Variable Object )
以下的例子有不同情況 :
- 多餘的變數,其預設為 undefined - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- // 建立執行環境 Execution Contexts 
 function fnA(a, b, c){
 console.log(a, b, c)
 }
 fnA(10)
 // 10 undefined undefined
 // 建立變數物件 VO
 // {
 // a:10
 // b:undefined
 // c:undefined
 // }
- 函式內建立函式,函式若與參數同名會被覆蓋 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14- // 執行環境 Execution Contexts 
 function fnA(a){
 function a(){}
 console.log(a)
 }
 function fnB(){return 1}
 fnA(fnB)
 // function a(){}
 // 參數 a 被函式覆蓋掉
 // VO variable object
 // {
 // a: function a(){}
 // }
- 總合以上情況並再次宣告參數然後賦予值 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20- function fnA(a, b, c){ 
 function b(){}
 console.log(a, b, c)
 // 10 function b(){} undefined
 var c = 5
 console.log(c)
 // 5
 }
 function fnB(){return 1}
 fnA(10, fnB)
 // 為什麼 c不是 5? 因為它宣告被忽略且呼叫時未被設定成 5
 // 在第二次呼叫 c 時,已被設定成 5
 // VO variable object
 // {
 // a: 10,
 // b: function b(){},
 // c: undefined
 // }
透過以上例子可以發現,當有執行環境後,就會有變數物件,而它在函式中會做以下三件事 :
- 若參數值為函式且參數名稱同名則覆蓋成函式
- 若函式內宣告且與參數名稱同名則忽略
- 參數名稱若有設定,預設值為 undefined
回到全域環境後,就更加單純,使用 var宣告的函式與變數會被覆蓋,若該變數未賦予值時,預設值為 undefined。
再回想一下,當我們寫好 JS程式碼後,透過瀏覽器執行,其實是開始創建接著執行,在創建(包含編譯)的過程中,Hosting 就是在建立變數物件的過程。
| 1 | // 撰寫 > 創建(包含建立變數物件) > 執行 | 
Temporal Dead Zone
目前的範例都是使用 var 來宣告,ES6開始就推薦以 let或 const 來宣告變數,那麼 let 與 const 所宣告的變數與函式也具有提升特性嗎?
| 1 | console.log(a) | 
上面錯誤的意義是,未初始化變數 a前,無法存取它的值,為什麼會這樣?
| 1 | // 執行環境 | 
為了解釋這個空窗期,於是就有了這個名詞 Temporal Dead Zone。拋出錯誤的目的還是希望開發者的撰寫習慣能改變吧?!