1.let命令
[ 基本用法 ]
let命令的用法與var類似,用於聲明一個變量,但是let聲明的變量只能在let所在的代碼塊內有效:
上述代碼塊中使用let和var聲明了兩個變量。然后在代碼塊之外調用這兩個變量,結果let聲明的變量報錯,var聲明的變量返回了正確的值。說明,let聲明的變量只在其所在代碼塊之內有效。
[ 不存在變量提升 ]
let不像var那樣會發生"變量提升"現象。所以,變量一定要在聲明后使用,否則報錯。
[ 暫時性死區 ]
如果區塊中存在let和const命令,則這個區塊對這些命令聲明的變量從一開始就形成封閉作用域。只要在聲明之前就使用這些變量就會報錯。這在語法上稱為"暫時性死區"簡稱TDZ。
上面的代碼中,在let命令聲明tmp之前,都屬於變量tmp的"死區"。有些"死區"比較隱蔽,不太容易發現:
報錯的原因是參數x默認等於參數y,而此時y還沒有聲明,屬於"死區"。如果聲明在使用之前就不會報錯:
總結:暫時性死區的本質就是,只要一進入當前作用域,所要使用的變量就已存在,但是不可獲取,只有等到聲明變量的那一行代碼出現,才可以獲取和使用該變量。
[ 不允許重復聲明 ]
let不允許在相同作用域內重復聲明同一個變量。
因此,不能在函數內部重新聲明參數。
2. 塊級作用域
ES5只有全局作用域和函數作用域,沒有塊級作用域,這會導致以下幾個不合理的場景:
- 第一種場景,內層變量可能會覆蓋外層變量。
函數f執行后,變量提升導致內層的tmp變量覆蓋了外層的tmp變量。
- 第二種場景,用來計數的循環變量泄露為全局變量。
變量i只用來控制循環,但是在循環結束之后它並沒有消失,而是泄露成為了全局變量。
let實際上為JavaScript新增了塊級作用域。
上面函數有兩個代碼塊,都聲明了變量n,運行后輸出5。表示外層代碼塊不受內層代碼塊的影響。如果使用var定義變量,則輸出10:
ES6允許塊級作用域任意嵌套,外層作用域無法讀取內層作用域的變量,但是內層作用域可以定義外層作用域的同名變量。
3. const命令
const用來聲明常量,一旦聲明,其值不能改變。
這意味着,一旦用const聲明一個常量,就必須立即初始化,不能留到以后賦值,如果只聲明不賦值就會報錯:
[ const ]
• 作用域與let命令相同:只在聲明所在的塊級作用域內有效。
• 聲明的常量也不能提升,同樣存在暫時性死區。
• 不可以重復聲明常量。
[ const聲明復合類型變量 ]
對於復合類型的變量,變量名不指向數據,而是指向數據所在的地址。const命令只是保證變量名指向的地址不變,而不保證該地址的數據不變。
上面代碼中,常量foo存儲的是一個地址,指向一個對象。不可變的只是這個地址,即不能把foo指向另一個地址,但對象本身是可變的,所以依然可以為其添加新屬性。但如果將另一個地址賦值給foo,就會報錯:
如果真的想將對象凍結,使其不可添加新屬性,應該使用Object.freeze方法。
給foo添加新屬性不起效果。
除了凍結對象本身,對象的屬性也應該一起凍結,下面是一個將對象徹底凍結的函數:
對象被徹底凍結之后,屬性也無法被修改。
4. 全局對象屬性
全局對象是最頂層的對象,在瀏覽器環境中指的是window對象。在ES5中,全局對象的屬性與全局變量是等價的。
這種規定容易在不知不覺中就創建了全局變量,ES6為了改變這一點,一方面規定,var命令和function命令聲明的全局變量依舊是全局對象的屬性;另一方面規定,let命令、const命令和class命令聲明的全局變量不屬於全局對象的屬性。