今天是鐵人賽的最後一天
我們要來認識重要的 Hoisting 提升
為什麼重要呢?
認識 Hoisting 之前,在宣告變數時不會太在意變數或函式的宣告要放在哪個位置,只要結果是我們想要的就好,也不確定會造成甚麼影響。但有沒有發現函式在被宣告的程式碼前也可以正常執行,或是使用 var
出現許多奇怪的現象,造成這些現象的原因就是 Hoisting
我們一樣先從程式碼來看,以下將會用 var
來宣告變數:
var Jack = "傑克";
console.log(Jack); // 傑克
這是一個簡單的變數宣告,並且正確地印出變數的值,我們現在把 console.log
移到前面:
console.log(Jack); // undefined
var Jack = "傑克";
出現了 undefined
!?
為什麼是 undefined
而不是 Jack is not defined
呢?
在使用 console.log
前 Jack
不是還沒被宣告嗎?
這時我們需要先了解宣告變數的步驟跟執行環境時做了甚麼事情
先回到前面的程式碼,並且拆開來看:
var Jack; // 先宣告變數
Jack = "傑克"; // 再賦予值
console.log(Jack); // 傑克
上面這段程式碼在執行時會分為兩個步驟
Jack
傑克
這個值賦予到變數上這是宣告變數時實際運作的流程
在建立一個執行環境時會分為兩個階段,首先會先「創造環境」,接著才是「執行」。
如下圖,在創造環境這一個階段時,會先把記憶體的 key 給放上去,如圖示先把變數 a
放到記憶體的左邊,但還不會給它值。所以這時候如果去檢查變數的時候,它的值會出現 undefined
。
到執行階段的時候才會把值給放上去,這時檢查就會出現 1
這個值。
創造環境時會先把程式碼所有的變數都挑出來,並且先在記憶體上面把它們的空間準備好,到執行的時候才會把這些程式碼依序執行並且賦予它的值:
在創造環境把記憶體空間準備好,這個流程就稱為「Hoisting 提升」。
再回到前面的程式碼看創造環境與執行階段:
var Jack; // 創造階段
Jack = "傑克"; // 執行
console.log(Jack); // 傑克
創造階段
var Jack; // Hoisting 提升
執行階段
console.log(Jack); // undefined
Jack = "傑克"; // 此時才被賦予值
這時就比較能了解甚麼是 Hoisting 與為什麼會出現這樣的結果
函式陳述式則有些不一樣,從以下程式碼看到不管在哪裡檢查都可以印出值
callName();
function callName(){
console.log("呼叫傑克"); // 呼叫傑克, 呼叫傑克
}
callName();
因為函式的陳述式會在創造階段就已經把它的記憶體以及它的函式都準備好了,且函式已包含所有內容。
以下一樣用創造環境與執行階段來拆解:
// 創造階段
function callName(){
console.log("呼叫傑克"); // 呼叫傑克, 呼叫傑克
}
//執行
callName(); // 呼叫傑克
callName(); // 呼叫傑克
現在我們換用函式表達式來試試:
var callName = function (){
console.log("呼叫傑克"); // 呼叫傑克
}
callName();
在後面執行當然是沒問題的
但是
callName(); // callName is not a function
var callName = function (){
console.log("呼叫傑克");
}
放到前面就會發生錯誤了,檢查時會發現 callName()
會是 undefined
一樣再用創造環境與執行階段來拆解:
// 創造階段
var callName;
// 執行
callName(); // 此時沒有值,檢查為 undefined
// 此時才把 function 賦予到變數上
callName = function (){
console.log("呼叫傑克");
}
所以我們可以得知如果使用函式表達式,我們必須先等函式已經賦予到值上面,才能運行該段函式。
因為 var
具有 Hoisting 特性,讓 var
有了「可以重複宣告」及「可以在使用之後才被宣告」這兩種令人頭痛的神奇特性。ES6 為了改善這些情況,於是出現了 let
跟 const
,它們最主要的差異是:
let
跟 const
是「區塊作用域」,而 var
是「函式作用域」var
的時候,宣告會被提前至作用域的前面,這個特性就是 Hoisting。let
跟 const
必須要宣告後才能被使用,相較於 var
更容易理解跟閱讀,於是在 ES6 後便提倡使用 let
跟 const
來取代 var
做宣告。但因為在維護專案或是看文章時還是可能有機會看到 var
的使用,且函式也具有 Hoisting 的特性,所以了解這些特性還是非常需要的。
參考資料
線上課程
MDN