ES6中的let、const

ES6中的let、const

引言

咱們知道JS這門語言有一套規定了它的基本語法的規範:ECMAscript。以前開發人員使用的都是ES5規範,後來,在2015年6月,ES6終於脫離了草案階段,正式發佈。在JS的ES5(ECMAscript5)規範中,變量的聲明就是varfunction這兩種,而這兩種變量聲明方式存在着一些不太合乎常理的問題。好比說很典型的一個問題:變量提高。以上兩種方式聲明變量都會把這個變量提高到當前做用域的最上面(這裏不詳細說細節),這其實並不合理,由於咱們聲明變量的目的就是去使用它,那麼咱們天然是想用它的時候去聲明,而後使用。還有一種不太好的地方:for循環裏面定義的計數變量是會泄露出去的,而咱們寫for循環實際上只想讓這個計數變量在當前的for循環內部使用。爲了解決這一系列的問題,在ES6中,新增了letconst聲明方式,固然也還有一些其餘的聲明方法先不提,咱們今天只說letconstjavascript

let

使用方法

let的使用和var同樣。java

let a = 0
複製代碼

而後就能夠訪問這個變量函數

console.log(a) // 0
複製代碼

特色

let聲明變量和var聲明變量有所區別:學習

不存在變量提高

ES5聲明變量:ui

console.log(a) // undefined,存在變量提高
var a = 0
複製代碼

let的:編碼

console.log(a) // a is not defined,不存在變量提高
let a = 0
複製代碼

由於var存在變量提高,打印undefined,而let不存在變量提高,直接就會報錯。spa

塊級做用域

在ES5裏面沒有塊級做用域的概念,咱們多經過當即執行函數來模仿一塊塊做用域,把這一塊的變量保護起來,同時也防止污染到全局。在ES6裏面,加入了塊級做用域的概念,就是說letconst聲明變量的時候也會把它們綁定到當前的代碼塊中,這個塊咱們稱之爲這個變量的塊級做用域,外部不可訪問,就相似於ES5裏面的函數內聲明的變量。指針

ES5:code

if (true) {
  var a = 0
  console.log(a) // 0
}
console.log(a) // 0
複製代碼

這裏面在代碼塊if裏面聲明的變量a做用域是全局,因此均可訪問。對象

ES6:

if (true) {
  let a = 0
  console.log(a) // 0
}
console.log(a) // a is not defined
複製代碼

在這裏面,let聲明的變量a只在這個代碼塊內有效,外部不可訪問,因此會報錯。這就是塊級做用域。這個塊級做用域有許多有意思的地方。請看下面的代碼:

function fn () {
    var a = 0
    if (true) {
      var a = 1
      console.log(a) // 1
    }
    console.log(a) // 1
  }
複製代碼

在ES5裏面,這樣寫的話,就至關於在if代碼塊裏面後定義的變量a覆蓋了前面定義的變量a,因此輸出的a的值都是1。那要是用ES6的let聲明呢?

function fn () {
    let a = 0
    if (true) {
      let a = 1
      console.log(a) // 1
    }
    console.log(a) // 0
  }
複製代碼

在這裏由於let聲明的變量只在當前的塊級做用域內有效,因此,在if代碼塊裏面打印a就是1,在外層打印a就是外層塊做用域a的值0,也就是說在這裏,父塊級做用域裏面的變量a用本身在當前塊級做用域內的值,子塊級做用域的變量a用本身當前塊做用域內的值,它們是互不影響的。那麼能夠引伸出一個let命令的很好的應用場景:for循環。

ES5裏面的for循環:

var a = []
  for (var i = 0; i < 10; i++) {
    a[i] = function () {
      console.log(i)
    }
  }
  a[2]() // 10
複製代碼

這裏面會打印出來10,由於定義在for循環內部的計數變量i其實會泄露爲全局變量,因此咱們在執行最後一段代碼時,訪問到的i就是全局的惟一的這個i,那麼這個i就會隨着循環改變,可是始終是這個惟一的i。這個時候for循環已經結束,i值變爲了10,因此咱們訪問哪一個都只會獲得10,可是這顯然不是咱們想要的,因此let來了。

var a = []
  for (let i = 0; i < 10; i++) {
    a[i] = function () {
      console.log(i)
    }
  }
  a[2]() // 2
複製代碼

這裏面就很特殊,let聲明打這個i只在for循環體內部可用,外部不能訪問,因此,這裏每一次循環的時候都訪問的是當前循環的i,就是說,每一次進入新的循環的時候,i都是一個新的變量i,不是以前的變量i,變量i只在本身當前的循環裏面有效,只是在JS的引擎裏面會記錄i值,後面的新的變量i就會從這個值開始繼續循環。因此,咱們這裏打印的i值就是2,由於它只能訪問它做用域裏面的那個變量i

for循環裏面還有一點要注意的就是:設置計數變量的部分是一個父做用域,循環體內部是一個子做用域。像下面這樣:

for (let i = 0; i < 5; i++) {
    let i = '我是子做用域'
    console.log(i)
  }
  // 我是子做用域
  // 我是子做用域
  // 我是子做用域
  // 我是子做用域
  // 我是子做用域
複製代碼

這裏面打印了五次我是子做用域,也就是說,設置計數變量的部分是一個父做用域,循環體內部是一個子做用域,他們是互不影響的,這個我前面有說過,各自用本身的塊做用域裏面的變量的值,就是說:計數變量的i就負責控制循環,循環體裏面的i就每次都打印一下就能夠了,各司其職。外面的塊級做用域不會影響到內層的塊級做用域,內層的塊級做用域也不會影響外面的塊級做用域,各自都是一個封閉的小區域。這裏面我總會想到CSS裏面的BFC區域(若是不瞭解BFC自行忽略這句話,沒什麼影響),和那個差很少。關於塊級做用域差很少就這些,咱們繼續說下面的內容。

暫時性死區(TDZ)

ES6明確規定,若是區塊中存在letconst命令,它所聲明的變量就會綁定到當前的塊級做用域上,不受外面的影響,這我前面也有說過,那麼這個塊級做用域就會造成封閉做用域,只要在聲明以前使用,就會報錯,這就是暫時性死區(TDZ)。最好理解的就是看下面的代碼:

if (true) {
    a = 2 // a is not defined
    console.log(a) // a is not defined
    let a
    console.log(a) // undefined
    a = 0
    console.log(a) // 0
  }
複製代碼

按照咱們以前所說,只要存在let,那麼這個變量a就是當前塊級做用域內有效,就造成封閉做用域了,因此咱們在聲明以前嘗試去給它賦值,打印它,都會報錯,由於咱們不能夠在聲明以前使用他們它,因此在這裏,在聲明前面的部分就是它的暫時性死區

某些隱蔽的暫時性死區:

function test (a = b, b = 0) {
    return [a, b]
  }
  test() // b is not defined
複製代碼

報錯的緣由:在變量b聲明以前嘗試去使用它,這屬於b的死區,因此報錯。

let a = a // a is not defined
複製代碼

咱們知道,賦值運算符先算右邊的內容,那麼這裏在聲明a以前嘗試獲取它,一樣屬於a的死區,報錯。

不容許重複聲明

let不容許在相同做用域內重複聲明同一個變量。注意:是相同做用域內。

let a = 1
  let a = 1 // Uncaught SyntaxError: Identifier 'a' has already been declared
複製代碼

函數內部也不能夠從新聲明參數:

function fn (arg) {
    let arg
  }

  fn() // Uncaught SyntaxError: Identifier 'arg' has already been declared
複製代碼

可是這樣作是能夠的:

function fn (arg) {
    {
      let arg
    }
  }

  fn() 
複製代碼

由於,這時聲明的變量就綁定到了當前塊級做用域上,不影響到外部,因此它和函數參數的那個變量的做用域也不同了,是被容許的。

const

使用方法

const使用方法和let基本一致。再也不贅述。

特色

前面的特性都和let同樣,這裏我只說區別。

常量

const聲明的是一個只讀的常量,一旦聲明,常量的值或者指向的地址不得改變。

const a = 1
  a = 2 // Uncaught TypeError: Assignment to constant variable.
複製代碼

const因爲這個特性,因此它聲明常量的同時必須馬上初始化(賦值),不然報錯。

const a // Uncaught SyntaxError: Missing initializer in const declaration
複製代碼

不可變性

實際上const聲明常量的不可變是分狀況的,若是是簡單數據類型的,則值不可變,若是是複雜類型的,則指針不可變,實際對象的屬性和方法可變。解釋一下,簡單數據類型直接把值存儲在棧內存當中,因此這個值就不可再改變,複雜類型的數據在棧內存中只存儲了一個地址(指針),這個地址不可變,可是這個指向堆內存的地址指向的具體的對象是能夠改變的。好比這樣:

const obj = {}
  obj.name = 'reslicma'
  console.log(obj) // {name: "reslicma"}
複製代碼

咱們只須要知道,const聲明的常量保存在棧內存的值都不能改變,可是複雜型數據指向堆內存中具體的對象能夠改變就能夠了,至於這個棧和堆內存具體內容能夠看一下我上一篇文章,裏面很詳細的敘述了這兩者和區別和聯繫(硬核推薦。。。)。並且ES6學習的話,真的很推薦阮老師的《ES6標準入門》這本書。。。

總結

不是對知識點總結,是說一說ES6引入letconst的做用:之前使用var由於存在變量提高,會形成一些沒必要要的錯誤,或者一些意料以外的咱們不想要的錯誤,因此加入了letconst避免這類錯誤,同時也是爲了但願開發人員們養成良好的的編碼規範和編碼風格。

相關文章
相關標籤/搜索