1. 程式人生 > 實用技巧 >ES6之let和const用法

ES6之let和const用法

let 和 const 使用

最近在看阮一峰關於ES6入門這本書,書的內容有很多,無論是陣列還是物件都新拓展了很多方法很難記憶,
而且很多在實際開發中很難會用到,這裡我會一邊學習一邊整理一些我覺得經常用到並且很有必要學習的新
知識點想閱讀完整版es6的童鞋可以在每篇文章底部找到連結。

let的使用

基本用法

ES6 新增了let命令,用來宣告變數。它的用法類似於var,但是所宣告的變數,只在let命令所在的程式碼塊內有效。

  {
        var a = 10;
        let b = 20;
    }
    console.log(a) //10
    console.log(b) //會報錯 b is not defined

上面程式碼在程式碼塊之中,分別用let和var聲明瞭兩個變數。然後在程式碼塊之外呼叫這兩個變數
結果let宣告的變數報錯,var宣告的變數返回了正確的值。這表明,let宣告的變數只在它所在的程式碼塊有效。

   for (var i = 0; i < 10; i++) {}
    console.log(i) // 10
    for (let j = 0; j < 10; j++) {}
    console.log(j) // 報錯 j is not defined

上面程式碼中,計數器i只在for迴圈體內有效,在迴圈體外引用就會報錯。

 for (let i = 0; i < 3; i++) {
        let i = 'bcd'
        console.log(i)
    }

上面這段程式碼證明迴圈變數i與for迴圈內部的i不在一個作用域。

 var a = []
    for (var i = 0; i < 3; i++) {
        a[i] = function() {
            console.log(i)
        }
    }
    a[0]() // 3
    a[1]() // 3
    a[2]() // 3

上面程式碼中,變數i是var命令宣告的,在全域性範圍內都有效,所以全域性只有一個變數i。每一次迴圈,變數i的值都會發生改變,
而迴圈內被賦給陣列a的函式內部的console.log(i),裡面的i指向的就是全域性的i。也就是說,所有陣列a的成員裡面的i,指
向的都是同一個i,導致執行時輸出的是最後一輪的i的值,也就是 3。
如果使用let,宣告的變數僅在塊級作用域內有效,最後分別輸出的是 0 1 2。

let不存在變數提升

var命令會發生“變數提升”現象,即變數可以在宣告之前使用,值為undefined。這種現象多多少少是有些奇怪的,按照一般的邏輯,變數應該在宣告語句之後才可以使用。
為了糾正這種現象,let命令改變了語法行為,它所宣告的變數一定要在聲明後使用,否則報錯。

console.log(a) //undefined
    var a = 10

    console.log(b) //報錯
    let b = 10

上面程式碼中,變數a用var命令宣告,會發生變數提升,即指令碼開始執行時,變數a已經存在了,但是沒有值,所以會輸出undefined。變數b用let命令宣告,不會發生變數提升。這表示在宣告它之前,變數b是不存在的,這時如果用到它,就會丟擲一個錯誤。

暫時性死區

只要塊級作用域記憶體在let命令,它所宣告的變數就“繫結”(binding)這個區域,不再受外部的影響。

 var temp = 123
   if(true){
       temp = 'abc'
       let temp
   }

上面程式碼中,存在全域性變數tmp,但是塊級作用域內let又聲明瞭一個區域性變數tmp,導致後者繫結這個塊級作用域,所以在let宣告變數前,對tmp賦值會報錯。

ES6 明確規定,如果區塊中存在let和const命令,這個區塊對這些命令宣告的變數,從一開始就形成了封閉作用域。凡是在宣告之前就使用這些變數,就會報錯。

總之,在程式碼塊內,使用let命令宣告變數之前,該變數都是不可用的。這在語法上,稱為“暫時性死區”(temporal dead zone,簡稱 TDZ)。

ES6 規定暫時性死區和let、const語句不出現變數提升,主要是為了減少執行時錯誤,防止在變數宣告前就使用這個變數,從而導致意料之外的行為。這樣的錯誤在 ES5 是很常見的,現在有了這種規定,避免此類錯誤就很容易了。

總之,暫時性死區的本質就是,只要一進入當前作用域,所要使用的變數就已經存在了,但是不可獲取,只有等到宣告變數的那一行程式碼出現,才可以獲取和使用該變數。

不允許重複宣告

// 報錯
function func() {
  let a = 10;
  var a = 1;
}

// 報錯
function func() {
  let a = 10;
  let a = 1;
}

因此,不能在函式內部重新宣告引數。

function func(arg) {
  let arg;
}
func() // 報錯

function func(arg) {
  {
    let arg;
  }
}
func() // 不報錯

const的使用

基本用法

const宣告一個只讀的常量。一旦宣告,常量的值就不能改變。

const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.

上面程式碼表明改變常量的值會報錯。

const宣告的變數不得改變值,這意味著,const一旦宣告變數,就必須立即初始化,不能留到以後賦值。

const foo;
// SyntaxError: Missing initializer in const declaration

上面程式碼表示,對於const來說,只宣告不賦值,就會報錯。

const的作用域與let命令相同:只在宣告所在的塊級作用域內有效。

if (true) {
  const MAX = 5;
}

MAX // Uncaught ReferenceError: MAX is not defined

const命令宣告的常量也是不提升,同樣存在暫時性死區,只能在宣告的位置後面使用。

本質

const實際上保證的,並不是變數的值不得改動,而是變數指向的那個記憶體地址所儲存的資料不得改動。
對於簡單型別的資料(數值、字串、布林值),值就儲存在變數指向的那個記憶體地址,因此等同於常量。
但對於複合型別的資料(主要是物件和陣列),變數指向的記憶體地址,儲存的只是一個指向實際資料的指標,
const只能保證這個指標是固定的(即總是指向另一個固定的地址),至於它指向的資料結構是不是可變的,
就完全不能控制了。因此,將一個物件宣告為常量必須非常小心。

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 種宣告變數的方法。

參考資料

ECMAscript基礎入門