認識JavaScript中的Hoisting


Posted by Andy Tsai on 2020-04-23

先來看看這段程式

console.log(name) //undefined
var name = 'James'

執行結果是undefined

你可能會覺得奇怪,JS不是由上到下 一行一行執行的嗎,
那麼照理說,console.log(name) 執行時,name還沒被宣告,應該要報錯吧!?

沒錯 如果在其他語言可能就報錯了,但在Javascript裡可不是這麼一回事。
這個行為稱為 Hoisting(提升) ,是Javascript裡的一個特殊現象。

記憶體指派

事實上瀏覽器引擎在執行你的Javascript程式碼之前,還會先做好幾件事,

其中一件事就是記憶體指派的動作,這個階段它會找出所有的宣告的變數,為它們保留一個記憶體空間,但還不會指派值,只會會給初始值undefined,如果是函式宣吿,則會將整個函式 包含內容 都存入記憶體空間,

有了這個概念後,開頭那段程式碼我們就知道為什麼會是undefined了

至於為何叫做Hoisting(提升),是因為這些宣告 很像是被移到最頂端,所以就被稱之為提升了,它是一個隱喻,實際上程式碼並沒有真的被移動。

函式的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

有些文章可能會說const和let是沒有Hoisting的,
但事實上,const和let也是有Hoisting的,只是行為比較不一樣,
const和let一樣會被保留記憶體位置,關鍵差異是,不會像var一樣被指派預設值undefined,而是不允許使用。

所以在Hoisting到正式指派值的期間,會有一個變數已存在 但無法使用的情況,這個尷尬的期間,就稱為暫時性死區(Temporal Dead Zone)。

了解後我們再來看這段code 就可以知道為何會報錯了

function test(){
    console.log(name)
    let name = 'James'
}

test()

執行結果是 Uncaught ReferenceError: Cannot access 'name' before initialization
原因就是let在正式被指派變數之前,是無法使用的,這就是一個暫時性死區。

總結

Hoisting是Javascript裡一個很有趣的行為,了解它後會對我們寫JS時有滿大的幫助

如果想再更深入探究,可以看看huli大神的這篇文。
我知道你懂 hoisting,可是你了解到多深?

Reference:
You Don't Know JS: Scope & Closures,
Javascript語法概念三明治,
我知道你懂 hoisting,可是你了解到多深?


#hoisting #javascript #w3HexSchool







Related Posts

React 部落格實作

React 部落格實作

筆記、15 週瀏覽器導讀

筆記、15 週瀏覽器導讀

Web Framework- Express

Web Framework- Express


Comments