先來看看這段程式
a = 2;
var a;
console.log(a)
執行結果是2
疑 奇怪,程式不是由上到下一行一行執行的嗎,
那麼照理說,var a 在 a = 2 之後執行,應該會把a重新賦值,
結果應該是undefinded吧,怎麼會是2?
這又是Javascript奇妙的地方之一,也是今天的主題 Hoisting。
編譯器
事實上瀏覽器引擎在執行你的Javascript程式碼之前,會先編譯它。而編譯階段中,有個階段是找出所有的宣告,並把它們關聯到合適的範疇。
也就是說,我們任何的宣告,不管是變數、函式的,都會在程式執行之前,預先被處理,
所以當看到一個var a = 2時,Javascript實際上會將它拆成var a與a=2,
宣告:var a 在編譯階段預先被處理(被Hoisting)
賦值:a = 2 在執行時才會處理
有了這個概念後,一開始那段程式碼我們就可以這樣去思考
var a;
a = 2;
console.log(a)
這樣一看,就知道為什麼的結果會是2了!
由於這些宣告 很像是被移到最頂端,所以就有Hoisting這個詞的出現,它是一個隱喻。
Hoisting是依照範疇去執行
Hoisting不是把全部的宣告 都移到到全域範疇最頂端,它會依照所處的範疇進行Hoisting。
foo()
function foo(){
console.log(a);
var a = 2;
}
實際上它執行時是這樣子
function foo(){
var a;
console.log(a); //undefined
a = 2;
}
foo()
函式的Hoisting
兩個重點,
- 只有函式宣告式(Function Declaration)會被Hoisting,運算式(Function Expressions)不會
- 整個函式都會被Hoisting。
看看這段code,你覺得它會不會報錯?
foo();
var foo = function(){
console.log('hi')
}
答案是會,執行結果是 Uncaught TypeError: foo is not a function,
因為運算式不會被Hoisting,所以我們是在調用undefined,它是一個TypeError的非法作業。
改成函式宣告式就合法了
foo();
function foo() {
console.log('hi')
}
函式優先
函式宣告會先被拉升,再來才是變數。
範例:
foo();
function foo(){
console.log('1');
}
var foo = function(){
console.log('2');
}
結果會是1,因為函式優先
Let、Const的Hoisting
我以前以為Let和Const是沒有Hoisting的,
後來看了huli大大的這篇後,才知道原來不是這樣
我知道你懂 hoisting,可是你了解到多深?
事實上,Let和Const也是有Hoisting的,只是行為比較不一樣
var a = 'hello'
function foo(){
console.log(a);
let a;
}
foo();
如果沒有Hoisting,這段code結果應該是hello,因為它會去取到外層a的值,
但執行結果卻是 Uncaught ReferenceError: a is not defined
原因是,let和const還是會Hoisting,只是沒有被初始化賦予undefined,所以取值會發生錯誤,
而這段在Hoisting之後到賦值之前的期間,稱為暫時性死區(Temporal Dead Zone)
大家如果想更深入了解Hoisting可以去看看那篇huli大大的文章。
Reference:
You Don't Know JS: Scope & Closures,
我知道你懂 hoisting,可是你了解到多深?