ES6之let和const的區別
let:宣告的是變數
1、不存在變數提升
// var 的情況
console.log(foo); // 輸出undefined
var foo = 2;
// let 的情況
console.log(bar); // 報錯ReferenceError
let bar = 2;
上面程式碼中,變數foo用var宣告,會發生變數提升,即指令碼開始執行時,變數foo已經存在了,但是沒有值,所以會輸出undefined。變數用let宣告,不會發生變數提升。這表示在宣告它之前,變數bar是不存在的,這時如果用到他,就會丟擲一個錯誤。
2、暫時性死區,先宣告在使用
var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError let tmp; }
上面程式碼中,存在全域性變數tmp,但是塊級作用域內let又聲明瞭一個區域性變數tmp,導致後者繫結這個塊級作用域,所以在let宣告變數前,對tmp賦值報錯,先宣告再使用。
3、不允許重複宣告
let
不允許在相同作用域內,重複宣告同一個變數。
// 報錯
function func() {
let a = 10;
var a = 1;
}
// 報錯
function func() {
let a = 10;
let a = 1;
}
4、塊級作用域
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只用來控制迴圈,但是迴圈結束後,它並沒有消失,洩漏成了全部變數。
ES6的塊級作用域,let實際上為javascript新增的塊級作用域。
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
上邊程式碼,有兩個程式碼塊,都聲明瞭變數n,執行後輸出5。這表示外層程式碼塊不受內層程式碼塊的影響。如果兩次都使用var定義變數n,最後輸出的值才是10。
ES6 允許塊級作用域的任意巢狀。
{{{{{let insane = 'Hello World'}}}}};
const:常量
const
宣告一個只讀的常量。一旦宣告,常量的值就不能改變。
const
的作用域與let
命令相同:只在宣告所在的塊級作用域內有效
const
命令宣告的常量也是不提升,同樣存在暫時性死區,只能在宣告的位置後面使用。
const
宣告的常量,也與let
一樣不可重複宣告。
const foo = {};
// 為 foo 新增一個屬性,可以成功
foo.prop = 123;
foo.prop // 123
// 將 foo 指向另一個物件,就會報錯
foo = {}; // TypeError: "foo" is read-only
上面程式碼中,常量foo
儲存的是一個地址,這個地址指向一個物件。不可變的只是這個地址,即不能把foo
指向另一個地址,但物件本身是可變的,所以依然可以為其新增新屬性
ES6 宣告變數的六種方法
ES5 只有兩種宣告變數的方法:var
命令和function
命令。ES6 除了新增let
和const
命令,後面章節還會提到,另外兩種宣告變數的方法:import
命令和class
命令。所以,ES6 一共有 6 種宣告變數的方法。
let和const的相同點:
① 只在宣告所在的塊級作用域內有效。
② 不提升,同時存在暫時性死區,只能在宣告的位置後面使用。
③ 不可重複宣告。
let和const的不同點:
① let宣告的變數可以改變,值和型別都可以改變;const宣告的常量不可以改變,這意味著,const一旦宣告,就必須立即初始化,不能以後再賦值
const i ; // 報錯,一旦宣告,就必須立即初始化
const j = 5;
j = 10; // 報錯,常量不可以改變
② 陣列和物件等複合型別的變數,變數名不指向資料,而是指向資料所在的地址。const只保證變數名指向的地址不變,並不保證該地址的資料不變,所以將一個複合型別的變數宣告為常量必須非常小心。
const arr = [];
// 報錯,[1,2,3]與[]不是同一個地址
arr = [1,2,3];
const arr = [];
// 不報錯,變數名arr指向的地址不變,只是資料改變
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
console.log(arr.length); // 輸出:3
若想讓定義的物件或陣列的資料也不能改變,可以使用object.freeze(arr)進行凍結。凍結指的是不能向這個物件或陣列新增新的屬性,不能修改已有屬性的值,不能刪除已有屬性。
const arr = [];
Object.freeze(arr);
// 不報錯,但資料改變無效
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
console.log(arr.length); // 輸出:0