深刻理解ES6之《塊級做用域綁定》

衆所周知,js中的var聲明存在變量提高機制,所以ESMAScript 6引用了塊級做用域來強化對變量生命週期的控制
let const 聲明不會被提高,有幾個須要注意的點
一、不能被重複聲明瀏覽器

假設做用域中已經存在某個標識符(不管該標識符是經過var聲明仍是let、const變量聲明),此時再使用let或const關鍵定聲明會拋錯
var count=10
let count=20// 此處則會拋出錯誤,由於同一做用域內不能重複聲明

若是當前做用域內嵌另外一個做用域,即可在內嵌的做用域中用let聲明同名變量函數

var count=10
if(true){
    let count=20
}

二、const聲明的常量必須進行初始化
像下面這樣聲明就會報錯spa

const name;//語法錯誤,常量未初始化

三、不能夠爲const定義的常量再賦值,真正的本質是const聲明不容許修改綁定,但容許修改值(也就是說const聲明對象後,能夠修改該對象的屬性值)code

const person={
  name:'angela'
}
//能夠修改對象屬性的值
person.name='yun'
// 修改綁定則會拋出語法錯誤
person={
  'name':'Shining'
}

四、臨時性死區(Temporal Dead Zone)
JavaScript引擎在掃描代碼發現變量聲明時,要麼將它們提高至做用域頂部(遇到var聲明),要麼將聲明放到TDZ中(遇到let和const聲明),訪問TDZ中的變量會觸發運行時錯誤,只有執行過變量聲明語句後,變量纔會從TDZ中移出,才能正常訪問
以下代碼就由於在if塊級做用域內執行console.log時value已經在TDZ中了,之前typeof是一個相對不易出錯的操做符,但其實也沒法阻擋引擎拋出錯誤
在聲明前訪問塊級綁定會致使錯誤,由於綁定在臨時死區中對象

if (true) {
  console.log(typeof value)//引用錯誤
  let value = 'blue'
}

而在let聲明的做用域外對該變量使用typeof則不會報錯生命週期

console.log(typeof value)
if (true) {  
  let value = 'blue'
}

五、塊級做用域綁定
以前在循環中建立函數都有些不可言狀圖片

var funcs = []
for (var i = 0; i < 10; i++) {
  funcs.push(function () {
    console.log(i)
  })
}
funcs.forEach(function (func) {
  func()
})

由於循環內部建立的函數所有都保留了對相同變量的引用,循環結束時變量i的值爲10,因此結果會輸出10次10
因而你們會在循環中使用當即調用函數表達式,以強制生成計數器變量的副本,以便輸出一、二、3......ip

var funcs = []
for (var i = 0; i < 10; i++) {

  funcs.push((function (value) {
    return function () {
      console.log(value)
    }
  })(i))

}
funcs.forEach(function (func) {
  func()
})

有了let,當即調用函數表達式則能夠簡化,其實每次迭代循環都會建立一個新變量,並以以前迭代中同名變量的值將其初始化作用域

var funcs = []
for (let i = 0; i < 10; i++) {
//實際上是每次循環的時候let聲明都會建立一個新變量i並將其初始化爲i的當前值,因此在循環內部建立的每一個函數都能獲得屬於它們本身的i的副本
  funcs.push(function () {
    console.log(i)
  })
}
funcs.forEach(function (func) {
  func()//這裏輸出是0 而後是一、2....9
})

這個特性一樣適用於for in中,舉例來講it

var funcs = [],
  obj = {
    a: true,
    b: true,
    c: true
  }
for (let key in obj) {
  funcs.push(function () {
    console.log(key)
  })
}
funcs.forEach(function (func) {
  func()//輸出的是a  b  c
})

六、循環中的let聲明特性一樣適用於const聲明,惟一的區別是const不能更改綁定
上面的例子中把let換成const一樣輸出a b c

var funcs = [],
  obj = {
    a: true,
    b: true,
    c: true
  }
//之因此能夠運用for in 和for of循環中,是由於每次迭代不會修改已有綁定,而是會建立一個新綁定
for (const key in obj) {
  funcs.push(function () {
    console.log(key)// 一樣輸出a b c  惟一的區別是循環內不能更改key的值
  })
}
funcs.forEach(function (func) {
  func()
})

下面的這個例子則會報錯,由於在for循環中更改了i的綁定而const常量不能更改綁定

var funcs = []
for (const i = 0; i < 10; i++) {
  funcs.push(function () {
    console.log(i)
  })
}
funcs.forEach(function (func) {
  func()
})

七、全局做用域綁定
當var被做用於全局做用域時,它會建立一個新的全局變量做用全局對象(瀏覽器環境中的window對象)的屬性,這意味着用var極可能會無心中覆蓋一個已經存在的全局變量
圖片描述
從上圖代碼中看出即使是全局對象RegExp Array都會被覆蓋
可是let或const會在全局做用域下建立一個新的綁定,但該綁定不會添加爲全局對象的屬性,換句話說用let或const不能覆蓋全局變量,而只能遮蔽它
圖片描述

這個時候的RegExp和window.RegExp是不相同的

let RegExp='hello'
console.log(RegExp) //hello
console.log(window.RegExp===RegExp)//false

const ncz='hi'
console.log(ncz)
console.log("ncz" in window)

最佳實踐:默認使用let而不是var默認使用const,只有確實須要改變變量的值時使用let

相關文章
相關標籤/搜索