1. 程式人生 > >ES6之let和const的區別

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 除了新增letconst命令,後面章節還會提到,另外兩種宣告變數的方法: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