1. 程式人生 > >《關於ES6的學習》

《關於ES6的學習》

實踐 對象 lar 大量 ade i++ ron 如果 time

var

JavaScript中,我們通常說的作用域是函數作用域,使用var聲明的變量,無論是在代碼的哪個地方聲明的,都會提升到當前作用域的最頂部,這種行為叫做變量提升(Hoisting)

也就是說,如果在函數內部聲明的變量,都會被提升到該函數開頭,而在全局聲明的變量,就會提升到全局作用域的頂部。

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的特點是不會變量提升,而是被鎖在當前塊中。

一個非常簡單的例子:

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

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

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無法汙染外部函數

在全局作用域聲明

如果在全局作用域使用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替代。

《關於ES6的學習》