分別呼叫這些例子,會產生甚麼結果?
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
20function 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。拋出錯誤的目的還是希望開發者的撰寫習慣能改變吧?!