《深刻理解ES6》筆記——塊級做用域綁定(1)

本章涉及3個知識點,var、let、const,如今讓咱們瞭解3個關鍵字的特性和使用方法。react

var

JavaScript中,咱們一般說的做用域是函數做用域,使用var聲明的變量,不管是在代碼的哪一個地方聲明的,都會提高到當前做用域的最頂部,這種行爲叫作變量提高(Hoisting)面試

也就是說,若是在函數內部聲明的變量,都會被提高到該函數開頭,而在全局聲明的變量,就會提高到全局做用域的頂部。segmentfault

function test() {
    console.log('1: ', a) //undefined
    if (false) {
      var a = 1
    }
    console.log('3: ', a) //undefined
}

test()

實際執行時,上面的代碼中的變量a會提高到函數頂部聲明,即便if語句的條件是false,也同樣不影響a變量提高。閉包

function test() {
    var a
    //a聲明沒有賦值
    console.log('1: ', a) //undefined
    if (false) {
      a = 1
    }
    //a聲明沒有賦值
    console.log('3: ', a) //undefined
}

在函數嵌套函數的場景下,變量只會提高到最近的一個函數頂部,而不會。異步

//b提高到函數a頂部,但不會提高到函數test。
function test() {
    function a() {
      if (false) {
        var b = 2
      }
    }
    console.log('b: ', b)
}

test() //b is not defined

若是a沒有聲明,那麼就會報錯,沒有聲明和聲明後沒有賦值是不同的,這點必定要區分開,有助於咱們找bug。函數

//a沒有聲明的狀況
a is not defined

let

let和const都可以聲明塊級做用域,用法和var是相似的,let的特色是不會變量提高,而是被鎖在當前塊中。code

一個很是簡單的例子:對象

function test() {
    if(true) {
      console.log(a)//TDZ,俗稱臨時死區,用來描述變量不提高的現象
      let a = 1
    }
}
test()  // a is not defined

function test() {
    if(true) {
      let a = 1
    }
    console.log(a)
}    
test() // a is not defined

惟一正確的使用方法:先聲明,再訪問。ip

function test() {
    if(true) {
      let a = 1
      console.log(a)
    }
}
test() // 1

const

聲明常量,一旦聲明,不可更改,並且常量必須初始化賦值。作用域

const type = "ACTION"

咱們試試從新聲明type,看看會報什麼錯:

const type = "ACTION"
type = 1
console.log(type) //"type" is read-only

const type = "ACTION"
let type = 1
console.log(type) //Duplicate declaration "type"

const雖然是常量,不容許修改默認賦值,但若是定義的是對象Object,那麼能夠修改對象內部的屬性值。

const type = {
  a: 1
}
type.a = 2 //沒有直接修改type的值,而是修改type.a的屬性值,這是容許的。
console.log(type) // {a: 2}

const和let的異同點

相同點:const和let都是在當前塊內有效,執行到塊外會被銷燬,也不存在變量提高(TDZ),不能重複聲明。

不一樣點:const不能再賦值,let聲明的變量能夠重複賦值。

臨時死區(TDZ)

上面咱們也提到了TDZ的場景,那麼,有什麼用呢?答案就是沒什麼用。

臨時死區的意思是在當前做用域的塊內,在聲明變量前的區域叫作臨時死區。

if (true) {
  //這塊區域是TDZ
  let a = 1
}

塊級做用域的使用場景

除了上面提到的經常使用聲明方式,咱們還能夠在循環中使用,最出名的一道面試題:循環中定時器閉包的考題

在for循環中使用var聲明的循環變量,會跳出循環體污染當前的函數。

for(var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i) //5, 5, 5, 5, 5
  }, 0)
}
console.log(i) //5 i跳出循環體污染外部函數

//將var改爲let以後
for(let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i) // 0,1,2,3,4
  }, 0)
}
console.log(i)//i is not defined i沒法污染外部函數

關於這個使用場景的具體分析能夠查看我寫的另一篇文章:JavaScript同步、異步、回調執行順序之經典閉包setTimeout面試題分析

在全局做用域聲明

若是在全局做用域使用let或者const聲明,當聲明的變量自己就是全局屬性,好比closed。只會覆蓋該全局變量,而不會替換它。

window.closed = false
let closed = true

closed // true
window.closed // false

最佳實踐

在實際開發中,咱們選擇使用var、let仍是const,取決於咱們的變量是否是須要更新,一般咱們但願變量保證不被惡意修改,而使用大量的const,在react中,props傳遞的對象是不可更改的,因此使用const聲明,聲明一個對象的時候,也推薦使用const,當你須要修改聲明的變量值時,使用let,var能用的場景均可以使用let替代。

=> 返回文章目錄

相關文章
相關標籤/搜索