[JavaScript] Javascript 中的 Hoisting(提升):幫你留位子
Outline:
- Hoisting 是什麼?
- let const and Hoisting
- Temporal dead zone (TDZ)
- Quiz time!
Hoisting 是什麼?
先看一段程式碼:
console.log(a);
在尚未宣告 a 變數之下,呼叫 a 變數會得到「a 還沒被宣告」這樣的錯誤:
我們知道 Javascript 是一行一行依序執行的語言,如果我把宣告 a 變數這行加在 console 下面,照理說還沒有跑到定義那一行,又呼叫 a ,應該會出現一樣的錯誤:
console.log(a);
var a = 2;
結果卻是回傳了 undefined
:
如果是函式,一樣在宣告函式之前呼叫他:
name();
function name(){
return 'emma'
}
也回傳了正確的值:emma
以上的現象,就是 Hoisting,我喜歡用一句話記憶 Hoisting 的概念:「幫你留位子」,因為 Hoisting 就是 Javascript 在執行任何程式碼之前,先把宣告的變數和函式放進記憶體空間裡,就像是事先幫他們留個位子的感覺。而變數和函數的 Hoisting 略有不同,以下來看幾個 Hoisting 的特性:
1. 變數的宣告 (Declarations) 會被提升
a=1;console.log(a); // 1
var a;
在這裡我們在 console 的下面才宣告 a,但是因為宣告會被提升,在執行任何程式碼之前,Javascript 就幫他留了位子,所以其實執行起來就像是這樣:
var a;
a = 1;
console.log(a); // 1
var a
並不是真的在底層執行時被搬到程式碼的最前面,只是看起來很像是被搬到最前面
2. 變數的初始化 (Initialization) 不會被提升
初始化就是賦值的意思,看範例:
var a;
console.log(a); // undefined
a = 1;
從這個範例就看得出來,賦值沒有被提升,要等到程式執行到這行了才會執行 a=1。
undefined
跟is not defined
是不一樣的錯誤,undefined
的意思是「我不知道他的值是多少」,is not defined
是「未宣告」的意思,Javascript 給已宣告未賦值的變數或是函數的預設值都是undefined
3. 函式陳述式 (function declaration) 會被提升
callMe('emma'); // hello emmafunction callMe(name){
console.log('hello '+name);
}
函式陳述式在宣告之前就被呼叫了,並可以成功執行。
4. 函式運算式 (function expression) 不會被提升
console.log(myName); // undefined
myName('emma'); // myName is not a functionvar myName = function(name){
console.log('i am + name);
}
這種以 var 來宣告的函式,稱為函式運算式,宣告會被提升,但是函式的內容不會。
5. 函式與變數同名,函式優先
console.log(aa); // fn a(){}var aa;
function aa(){};
如果變數和函式同名,函式將被提升,並優先權較高,並如果多個函式同名,後面的會覆蓋前面的。
6. 函式內的參數不會被提升 (函式範疇 function scope)
console.log(aa); // ReferenceError: aa is not defined
function age() {
var aa = 18;
}
在函式內以 var 宣告的變數,因為 var 本身函式範疇 (function scope) 特性的關係,可取範圍只有在函式內,不會被提升至外面。
let, const and Hoisting
在 W3C 中是這樣說的:
Variables and constants declared with let or const are not hoisted!
> 由 let 和 const 宣告的變數不會被提升
更精準一點說,let & const 在宣告時其實有被提升,但是跟一般以 var 宣告變數的提升方式不太一樣,差別在於 let & const 提升後在未被初始化 (Initialization) 之前不可使用,意思就是 let const 宣告之後,在賦值之前,不能被取用。
暫時死區 ( Temporal dead zone )
在 let & const 宣告後未被賦值之前不可取用的期間,我們就稱為「暫時死區(Temporal dead zone)」,在這期間如果要取用 let & const 宣告的變數,Javascript 會回傳 ReferenceError
的錯誤。
console.log(name); // Uncaught ReferenceError: name is not defined
let name = 'emma';
📝 Quiz time !
以下取自 JavaScript Questions 中有關 hoisting 的小測驗,希望看完上面的說明,下面的題目可以讓你迎刃而解:
What’s the output?
function sayHi() {
console.log(name);
console.log(age);
var name = 'Lydia';
let age = 21;
}sayHi();
- A:
Lydia
andundefined
- B:
Lydia
andReferenceError
- C:
ReferenceError
and21
- D:
undefined
andReferenceError
— — — — — — — — — — — —
— — — — — — — — — — — —
— — 仔細想想,不要偷看 — -
— — — — — — — — — — — —
— — — — — — — — — — — —
Answer: D
name 這個變數在函式內獲得提升,且是由 var 所宣告,所以會回傳 undefined
,age 變數也在函式內獲得提升,但是在呼叫他的時候尚未賦值,且 age 為 let 所宣告,表示 age 仍在 TDZ 期間,故 Javascript 拋回 ReferenceError
錯誤。
答對了嗎?
內容若有任何錯誤,歡迎留言交流指教! 🐧