iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 6
1
Modern Web

JavaScript 原力覺醒 - 成為絕地武士之路系列 第 6

JS 原力覺醒 Day06 - 提升 Hoisting

一路上感謝各位讀者們的支持和回饋。
本 30 天系列文目前已經將篇幅重新整理、編纂成冊。
《JavaScript 概念三明治》在天瓏書局上架囉!
喜歡這個系列,想閱讀更詳細原理說明的讀者可以參考:
https://www.tenlong.com.tw/products/9789864347575

今天我們要提到另外一個講到 JS 一定會提到的概念,就是提升 ( Hoisting ),提升是 JavaScript 裡面特有的行為,指的是在宣告一個變數或是函式之前,就先使用它,而且不會出錯,懂了這個概念之後,很多疑雲應該也會迎刃而解。

Outline

執行環境創造

JS 最特別的地方就是在你要使用一個變數之前,不一定要先宣告,只要在相同環境底下的其他地方有宣告,就不會發生錯誤,常見的其他語言如果不先宣告,通常就會出錯。這個現象對其他語言的使用者來說,可能會有點疑惑,不過當我們了解底層語法解析器的運作模式之後,就不會那麼難以理解了。

c3po() 
console.log(greeting) // undefined

var greeting = 'hello , master.'
function c3po () {

  console.log('c-3po has been called!')

} 

上面這段程式碼不會出錯,看起來就像底下的變數跟函式宣告被拉到這段程式碼的最上面,

https://ithelp.ithome.com.tw/upload/images/20190921/201065801xX61szKXJ.jpg

回想一下前面我們講到 V8 引擎,全域執行環境產生之後會做兩件事情:

  1. 產生全域物件 window
  2. 產生 this 物件

記憶體空間的指派

其實不只這樣,在執行環境裡面用到的變數跟函式,總要有地方存放,所以在這個階段還會做幾件事情:

  1. 會為所宣告的變數保留記憶體空間,但還不會指派程式碼寫入的值,只會給初始值 undefined。

  2. 也會為一般的函式宣告(使用 function 關鍵字宣告的具名函式)保留記憶體空間,且會將整個函式內容存入記憶體空間。

所以這個變數記憶體空間被保留到哪裡?這個地方就是全域記憶體 ( Global Memory ) ,或稱記憶體堆積 ( Heap )。

https://ithelp.ithome.com.tw/upload/images/20190921/20106580qgs8eG6Kb4.jpg

在這個階段,執行環境正好「剛產生」後,所有宣告的變數都只有做保留記憶體空間的動作,還沒有被赴值,因此也被稱為「創造階段」。創造階段結束後,就會進入「執行階段」,直到這個時候前面宣告的變數才會被赴值,程式碼才會真的被執行。

執行環境與提升

上述在「創造階段」所做的,為變數及函式保留記憶體空間的動作,就被稱為「提升( Hoisting )」。提升這個動作在不論是全域執行環境還是函式執行環境,所有的執行環境都會進行。

let 、const 的提升

let 、const 兩個是在 ES6 之後才出現,用來宣告變數的關鍵字,這兩個關鍵字沒辦法像使用 var 那樣,在函式宣告之前就使用,乍看之下,在這兩個變數上並不會有提升的動作。但是其實是有的,只是 JS 在變數正式被賦值之前不讓你使用而已,在同一執行環境底下,使用 let 宣告變數的語彙環境(實際位置)之前的區域,被稱為 TDZ (Temporal Dead Zone) 。

console.log(name) // Reference Error! 
function doSomeThing( ){
	console.log(name) // Reference Error!  
}
doSomeThing()  
// 在 let 、 const 變數宣告正式發生前,都無法取用(TDZ)
let name = 'Luke'

所以這邊只要知道, 使用 let 、 const 宣告變數雖然還是有提升的作用,但是還是不能像 var 那樣自由的使用,等於有跟沒有一樣。 而我認為這樣子的限制也能減少開發者寫出讓人誤會的程式碼的機會,算是一個好處。


上一篇
JS 原力覺醒 Day05 - Scope Chain
下一篇
JS 原力覺醒 Day07 - 陳述式 表達式
系列文
JavaScript 原力覺醒 - 成為絕地武士之路30

2 則留言

2
阿展展展
iT邦好手 1 級 ‧ 2019-12-18 09:07:13

等於有跟沒有一樣 /images/emoticon/emoticon37.gif

0
bushigi5913
iT邦新手 5 級 ‧ 2021-08-10 22:00:47
hello() 
console.log(greeting) // undefined

var greeting = 'hello , master.'
function c3po () {

  console.log('c-3po has been called!')

} 

位於outline這段程式碼是否有誤呢?您說這段程式碼不會出錯,但hello is undefined呀,是筆誤嗎@@?

Spark iT邦新手 5 級 ‧ 2021-09-04 21:19:42 檢舉

您好,是的,不好意思喔,上述的 hello 應為 "c3po"
已經修正了,感謝提醒!

我要留言

立即登入留言