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,可是你了解到多深?
🚀實體工作坊分享
玩轉 Web頁面的前端技術(HTML/CSS/JS) 一日體驗課
最近時賦學苑開了實體體驗課,即使你對程式碼沒有概念也能上手!Lala 會帶你一起做出一個個人品牌形象網站,帶你快速了解前端的開發流程,快跟我們一起玩轉 Web 吧!🚀線上課程分享
線上課程可以加速學習的時間,省去了不少看文件的時間XD,以下是我推薦的一些課程想學習更多關於前後端的線上課程,可以參考看看。
Hahow
Hahow 有各式各樣類型的課程,而且是無限次數觀看,對學生或上班族而言,不用擔心被時間綁住六角學院
如果你是初學者,非常推薦六角學院哦!剛開始轉職也是上了六角的課,非常的淺顯易懂,最重要的是,隨時還有線上的助教幫你解決問題!
Udemy
Udemy 裡的課程非常的多,品質普遍不錯,且價格都滿實惠的,CP值很高!也是很多工程師推薦的線上課程網站。