要解釋 Hoisting 最簡單的例子如下,結果會輸出 undefined
:
console.log(a); // => undefined
var a = 5;
咦很奇怪吧?
明明變數 a
在第二行才進行宣告,怎麼不是拋出個 error 訊息: a is not defined
,卻是輸出 undefined
呢?
這種奇怪的現象稱為 變數
/ 函式
提升 Hoisting。
想要最簡單理解的話,可以想像成 變數
或 function
的宣告提升到作用域的最上方,然而只有理解到這層面的話,只會記得 Hoisting 就是個奇怪的現象。
但如果從原理開始了解,就不會再有 Hoisting 是個神秘黑魔法的想法,變成:「哦,就只是因為某某機制導致的結果麻!」
所以本篇著重於介紹 Hoisting 的原理、以及在 Hoisting 當中發生名稱衝突的優先順序。
當了解 EC 的編譯及執行這兩個階段,就不難理解為什麼會有 Hoisting 的現象。
Hoisting 的重點就是因為有 EC 編譯階段,會先把 變數
跟 function
丟進 EC 的 VO 裡面
undefined
內容的記憶體位置
global EC: {
VO: {
a: undefined,
}
}
所以在執行階段時,當我們要查詢變數 a
,因為 EC 的 VO 裡面已有宣告 a
的紀錄,才會造成輸出結果為 undefined
。
這樣「 看似把 變數
& function
的宣告提到( 作用域 )最上面的動作,稱為 Hoisting 」,然而我們要是了解 JS 執行的過程,就可以很清楚解釋 Hoisting 的原理。
arguments
& Function
& var
,如何判斷最終提升的結果?function
提升的優先度會比變數
高,跟宣告順序沒關係:
(當然後面宣告的 function 會蓋過前面的 function)
function test() {
console.log(a);
function a() {};
var a = '1';
}
test(); // => [Function: a],是 Function 而非 undefined
function test(a) {
console.log(a);
var a = 456;
}
test(123); // => 123
test()
裡面已經有 a
變數了,所以變數 a => undifined
的提升 Hoisting 等於沒有用,但如果是直接賦值,等於是在執行階段改變了 a
的值,則當然會影響到:
function test(a) {
var a = 456;
console.log(a);
}
test(123); // => 456
function test(a) {
console.log(a);
function a() {
return 'a function';
}
}
test(123); // => [Function: a]
let
& const
呢?其實這兩種變數宣告方式也是有 Hoisting 的,只是方式不太一樣,let
& const
同樣會把變數提上去,但不會設成 undefined
,且在賦值之前無法存取,這範圍稱為 Temporal Dead Zone (TDZ)
還記得上一篇:打好基礎的第一步,從了解什麼是 EC 開始 提過 VO 初始內容的優先順序及覆蓋方式,所以也就能夠推斷出,當發生名稱衝突時、Hoisting 的優先度( 由高至低 ):
function declaration
function arguments
variable
function 提升是有其必要的,可以解決這種 A call B
& B call A
的互相呼叫 function 的問題。
以下範例取自 JavaScript系列文章:变量提升和函数提升:
// 验证偶数
function isEven(n) {
if (n === 0) {
return true;
}
return isOdd(n - 1);
}
// 验证奇数
function isOdd(n) {
if (n === 0) {
return false;
}
return isEven(n - 1);
}
console.log(isEven(2)); // true
以下內容完全取自:我知道你懂 hoisting,可是你了解到多深?
var a = 10
console.log(a)
上面這兩行有個差異,第一行的時候我們只需要知道「a 的記憶體位置在哪裡」就好,我們不關心它的值是什麼。
而第二行則是「我們只關心它的值是什麼,把值給我就好」,所以儘管兩行裡面都有a,但你可以看出來他們所要做的事情是不一樣的。
第一行的 a
我們叫它 LHS(Left hand side)引用,第二行叫它 RHS(Right hand side)引用,這邊的 left 跟 right 指的是相對於等號的左右邊,但用這種方式理解的話其實不夠精確,因此像下面這樣記就好:
以下有個小測驗,可以來檢驗對 Hoisting 的熟悉程度:
var a = 1;
function test(){
console.log('1.', a);
var a = 7;
console.log('2.', a);
a++;
var a;
inner();
console.log('4.', a);
function inner(){
console.log('3.', a);
a = 30;
b = 200;
}
}
test();
console.log('5.', a);
a = 70;
console.log('6.', a);
console.log('7.', b);
輸出結果如下:
1. undefined
2. 7
3. 8
4. 30
5. 1
6. 70
7. 200
參考資料:
( 以上內容大部分是 程式導師實驗計畫第三期 的學習筆記,如有錯誤歡迎糾正,非常感謝 🤓 )
Written on August 6th, 2019 by Yakim shu