let 、 const 、 塊級作用域
本文為學習筆記;
寫在前面
ES6 宣告變數的 6 種方法
ES5 只有兩種宣告變數的方法:var
命令和 function
命令。ES6 除了新增 let
和 const
命令,後面章節還會提到,另外兩種宣告變數的方法:import
命令和 class命
令。所以,ES6 一共有 6 種宣告變數的方法。
塊級作用域
為什麼需要塊級作用域
ES5 只有全域性作用域和函式作用域,沒有塊級作用域,這帶來很多不合理的場景。比如內層變數可能會覆蓋外層變數;用來計數的迴圈變數洩露為全域性變數
let
實際上為 JavaScript 新增了塊級作用域。
塊級作用域的出現,實際上使得獲得廣泛應用的立即執行函式表示式(IIFE)不再必要了。
塊級作用域與函式宣告
- 在瀏覽器的 ES6 環境中,塊級作用域內宣告的函式,行為類似於
var
宣告的變數; - 另外,還有一個需要注意的地方。ES6 的塊級作用域允許宣告函式的規則,只在使用大括號的情況下成立,如果沒有使用大括號,就會報錯。
let
不存在變數提升
let
所宣告的變數只在其所在的程式碼塊內有效let
不存在變數提
var
所宣告的變數在指令碼執行前就將變數都定義了,在指令碼中的賦值語句中再對其進行賦值操作,所以變數可以在宣告之前使用,值為undefined
let
糾正了這種語法行為,它所宣告的變數一定要在變數聲明後使用,否則報錯
暫時性死區(temporal dead zone
,簡稱 TDZ)
- ES6 中如果在區塊中存在
let
和const
,則區塊對這些命令宣告的變數從一開始就形成了封閉作用域,凡是在宣告之前使用這些變數都會報錯 - ES6 規定暫時性死區和
let
、const
語句不出現變數提升,主要是為了減少執行時錯誤,防止在變數宣告前就使用這個變數,從而導致意料之外的行為。這樣的錯誤在 ES5 是很常見的,現在有了這種規定,避免此類錯誤就很容易了。 在沒有let之前,typeof運算子是百分之百安全的,永遠不會報錯。現在這一點不成立了。這樣的設計是為了讓大家養成良好的程式設計習慣,變數一定要在宣告之後使用,否則就報錯。
ES6 規定暫時性死區和let、const語句不出現變數提升,主要是為了減少執行時錯誤,防止在變數宣告前就使用這個變數,從而導致意料之外的行為。這樣的錯誤在 ES5 是很常見的,現在有了這種規定,避免此類錯誤就很容易了。
總之,暫時性死區的本質就是,只要一進入當前作用域,所要使用的變數就已經存在了,但是不可獲取,只有等到宣告變數的那一行程式碼出現,才可以獲取和使用該變數。
不允許重複宣告
let
不允許在相同作用域內,重複宣告同一個變數。
const
const
宣告一個只讀的常量。一旦宣告,常量的值就不能改變;(這意味著,const
一旦宣告變數,就必須立即初始化,不能留到以後賦值)const
的作用域與let命令相同:只在宣告所在的塊級作用域內有效。const
命令宣告的常量也是不提升,同樣存在暫時性死區,只能在宣告的位置後面使用。
const 的本質
const
實際上保證的,並不是變數的值不得改動,而是變數指向的那個記憶體地址所儲存的資料不得改動。對於簡單型別的資料(數值、字串、布林值),值就儲存在變數指向的那個記憶體地址,因此等同於常量。但對於複合型別的資料(主要是物件和陣列),變數指向的記憶體地址,儲存的只是一個指向實際資料的指標,const
只能保證這個指標是固定的(即總是指向另一個固定的地址),至於它指向的資料結構是不是可變的,就完全不能控制了。因此,將一個物件宣告為常量必須非常小心。
如果真的想將物件凍結,應該使用Object.freeze方法。
const foo = Object.freeze({});
// 常規模式時,下面一行不起作用;
// 嚴格模式時,該行會報錯
foo.prop = 123;
上面程式碼中,常量foo指向一個凍結的物件,所以新增新屬性不起作用,嚴格模式時還會報錯。
除了將物件本身凍結,物件的屬性也應該凍結。下面是一個將物件徹底凍結的函式。
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};