認識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

.Net MVC authorization Controller and Workcontext extension in razor view

.Net MVC authorization Controller and Workcontext extension in razor view

JS30 Day 15 筆記

JS30 Day 15 筆記

用 tkinter 實現選擇路徑打開 excel ,並用 tree view 顯示

用 tkinter 實現選擇路徑打開 excel ,並用 tree view 顯示


Comments