ES6
1.0 let 和 const 命令
1.0 let 命令
ES6新增了
let
命令, 但是它與var
不同的是,let
所聲明的變量, 只在let
命令所在的代碼塊內有效.{ let a = '我是let聲明變量'; var b = '我是var聲明變量' } // console.log(a); // ReferenceError: a is not defined console.log(b);
let
聲明的變量不存在變量提升
存在暫時性死區
只要塊級作用域內 存在
let
命令, 它所聲明的變量就"綁定(binding)" 這個區域, 不再受外部影響var temp = 123 if (true) { temp = 'abc'; // ReferenceError: temp is not defined let temp console.log(temp); }
上面這段代碼中, 存在全局變量
temp
, 但是塊級作用域內let
又聲明瞭一個局部變量temp
, 導致塊級作用域內的局部變量temp
綁定這個塊級作用域, 全局變量temp
對塊級作用域無效, 所以在let
聲明變量之前, 對temp
賦值會報錯.ES6明確規定, 如果區塊中 存在
let
和const
命令 這個區塊對用let
和const
聲明的變量, 從一開始就形成了封閉作用域. 凡是在聲明變量之前就使用這些變量, 就會報錯.總之, 在代碼塊內, 使用
let
命令聲明變量之前, 該變量都是不可用的. 這在語法上, 成爲 “暫時性死區” (temporal dead zone, 簡稱 TDZ)if (true) { // TDZ開始 tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError let tmp; // TDZ結束 console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 }
上述代碼中, 在
let
命令聲明變量tmp
之前, 都屬於tmp
的 “死區”
2.0 塊級作用域
爲什麼需要塊級作用域?
ES5 只有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景
場景一: 內層變量可能會覆蓋外層變量
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = 'hello world'; } } f(); // undefined
上面代碼的原意是,
if
代碼塊的外部使用外層的tmp
變量,內部使用內層的tmp
變量。但是,函數f
執行後,輸出結果爲undefined
,原因在於變量提升,導致內層的tmp
變量覆蓋了外層的tmp
變量。場景二: 用來計數的循環變量泄露爲全局變量
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5
上面代碼中, 變量
i
只是用來控制循環, 但循環結束後, 它並沒有消失, 泄露成了全局變量. 這種方式不安全.
3.0 ES6 的塊級作用域
let
實際上爲 JavaScript新增了塊級作用域function f1() { let n = 5; if (true) { let n = 10; // 塊級作用域內, 不影響外部的同名變量 n } console.log(n); // 5 } f1()
4.0 const 命令
const
聲明一個只讀的常量, 一旦聲明, 常量的值不能改變.
const
聲明的變量不得改變值,這意味着,const
一旦聲明變量,就必須立即初始化,不能留到以後賦值。const foo; // SyntaxError: Missing initializer in const declaration
const
的作用域與let
命令相同:只在聲明所在的塊級作用域內有效const
命令聲明的常量也是不提升,同樣存在暫時性死區,只能在聲明的位置後面使用。
本質:
const
實際上保證的,並不是變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,因此等同於常量。但是: 對於複合類型的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針,
const
只能保證這個指針是固定的(即總是指向另一個固定的地址),至於它指向的數據結構是不是可變的,就完全不能控制了。因此,將一個對象聲明爲常量必須非常小心。const foo ={} foo.name = 'Gene' // 爲foo對象添加一個屬性, 可以添加成功 console.log(foo); // { name: 'Gene' } foo = {} // 將foo指向另一個值或對象就會報錯
上面代碼中,常量
foo
儲存的是一個地址,這個地址指向一個對象。不可變的只是這個地址,即不能把foo
指向另一個地址,但對象本身是可變的,所以依然可以爲其添加新屬性。
再看如下的例子
const a = []; a.push('Hello'); // 可執行 a.length = 0; // 可執行 a = ['Dave']; // 報錯
上面代碼中,常量
a
是一個數組,這個數組本身是可寫的,但是如果將另一個數組賦值給a
,就會報錯。
5.0 頂層對象的屬性
頂層對象,在瀏覽器環境指的是
window
對象,在 Node 指的是global
對象。ES5 之中,頂層對象的屬性與全局變量是等價的。window.a = 1; a // 1 a = 2; window.a // 2
上面代碼中,頂層對象的屬性賦值與全局變量的賦值,是同一件事。
總結:
該博客爲學習阮一峯 ES6入門課時所做的筆記記錄, 僅供留作筆記記錄學習和理解.
交流學習加 WeChat(備註技術交流學習):