在瞭解提升(Hoisting)之前,先讓我們看看 JavaScript 中變數的有效範圍(Scope)。
變數的有效範圍 (Scope)
在 ES6 之前,JavaScript 變數有效範圍的最小單位是以 function
做分界的。
1 | var x = 1; |
由於函式 fn()
裡面再次定義了變數 x
,所以當我們執行 fn(50)
時,會將 50
作為參數傳入 fn()
的 y
,那麼 return x + y
的結果自然就是 100 + 50
的 150
了。
因為「切分變數有效範圍的最小單位是 “function” 」,所以在函式區塊內透過 var
定義的 x
實際上只屬於這個函式。 換句話說,外面的 x
跟 function 內的 x
其實是兩個不同的變數,所以最後一行印出的 x 是 1 而不是 100 。
如果 function 內部沒有 var x
呢?
如果自己的 function 內如果找不到,就會一層層往外找,直到全域變數為止:
範例:
1 | var x = 1; |
要注意的是, function
可以讀取外層已經宣告的變數,
但外層拿不到裡面宣告的變數。
沒有 var
宣告的變數會變成全域變數
1 | var x = 1; |
由於在 function
內沒有重新宣告 x
變數,使得 x = 100
跑去變更了外層的同名變數 x
。
提升 (Hoisting)
一般在寫程式的時候,我們都會定義好變數,然後才去使用它,如果在尚未定義的情況下,直接去使用這個變項,通常都會出現錯誤訊息!
然而,在 javascript 中有一個蠻特別的概念是 hoisting ,指的是在 JavaScript 中,它會把定義的變數移到最前面先執行。
JavaScript 僅提升宣告的部分,而不是初始化。如果在使用該變數後才宣告和初始化,那麼該值將是 undefined。
1 | var x = 1; |
上面的例子,實際上在瀏覽器 (或者編譯器) 的眼中,是長這樣的:
1 | var x = 1; |
除了變數以外,函式也有提升。
透過「函式宣告」方式定義的函式可以在宣告前使用 (函式提升) :
1 | fn(2); // 4 |
而透過「函式運算式」定義的函式則是會出現錯誤:
1 | fn(2); // TypeError: square is not a function |
與變數提升的差別在於變數提升只有宣告被提升,而函式的提升則是包括內容完全被提升。