JS 在編譯時,會將宣告的變數、函式都先存放到一個記憶體空間,很像是被提升到最前面,賦值不會跟著被提升,實際運行時,再把值塞進剛剛存放的記憶體空間裡。
變數提升(Variable hoisting)
先來看個範例:
我 log 了一個從未宣告的變數 dog
1 | console.log(dog); |
會出現 Uncaught ReferenceError: dog is not defined 的錯誤,這非常合理!
那如果先 log 出下面的變數會發生什麼事?
1 | console.log(dog); |
出現了 undefined!!為什麼不是 ReferenceError: dog is not defined 呢?
這是因為宣告的變數先被存放到記憶體空間了,而賦值不會被提升,你可以想成這樣
1 | var dog; |
注意!var dog 並不是真的被放到最前面,而是記憶體空間
先存放了 dog,才會繼續依照執行順序運行程式碼,因此 log 會是 undefined,在執行程式碼之前,記憶體空間會是這樣
1 | globle scope: { |
在函式裡宣告的變數也會被放到 function scope 的記憶體空間
1 | function dog() { |
執行起來時會像是這樣:
1 | function dog() { |
函式提升(Function hoisting)
函式有分為函式陳述式(具名函式)、函式表達式(匿名函式)兩種,其中函式陳述式會被提升,函式表達式不會被提升,我們來看看範例。
函式陳述式
1 | dog(); |
結果會是 woof
dog()放在函式之前,卻可以 log 出我們要的結果,這是 hoisting 的關係,可以想成這樣
1 | function dog() { |
函式表達式
1 | dog(); |
結果變成 Uncaught TypeError: dog is not a function
這是因為宣告的變數先被提升了,會變成這個樣子
1 | var dog; |
我們在呼叫 dog 時還不是個 function,因此會噴出 TypeError 的錯誤
函式的 hoisting 可以輕易達成遞迴函式
1 | function even(n) { |
我們在 even 裡面呼叫 odd,在 odd 裡面也呼叫 even,這種遞迴狀況如果沒有 hoisting 是沒辦法解決的。
函式與變數同名,函式優先
1 | console.log(dog); // fn dog(){} |
函式的優先權比變數高,如有多個函式,將會覆蓋前面的
let 與 const 也有 hoisting
在上一篇var, let, const 概念就有講到, let、const 也是會有 hoisting 的,差別在於提升之後,var 宣告的變數會被初始化為 undefined,而 let 與 const 的宣告不會被初始化為 undefined,而且如果你在「賦值之前」就存取它,就會拋出錯誤。
在「提升之後」以及「賦值之前」這段期間被稱為「暫時死區」(Temporal Dead Zone,TDZ),可以幫助我們養成在變數還沒宣告之前,不要使用他的好習慣,讓程式碼可讀性提高。
hoisting 的筆記就告一段落,了解正確觀念真的很重要,歡迎路過多多指教,一起學習程式吧 😀
推薦閱讀: 我知道你懂 hoisting,可是你了解到多深?
Hey!想學習更多前端知識嗎?
最近 Lala 開了前端課程 👉【實地掌握RWD - 12小時新手實戰班】👈無論您是 0 基礎新手,又或是想學 RWD 的初學者,
我們將帶你從零開始,深入了解並掌握 RWD 響應式網頁設計的核心技術,快來一起看看吧 😊