認識JavaScript中的Hoisting


Posted by Andy Tsai on 2020-04-23

先來看看這段程式

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,可是你了解到多深?


#hoisting #javascript #w3HexSchool







Related Posts

簡明 Linux Shell Script 入門教學

簡明 Linux Shell Script 入門教學

5. 實際開發 ToDo List 案例

5. 實際開發 ToDo List 案例

MTR04_0903

MTR04_0903



Comments