Javascript塊級域內的函數聲明提升

主題

昨天我的一個技術交流羣裏發了一段代碼,涉及的是變量和函數的聲明提升,執行結果非常讓人迷惑,大家討論許久,還是有地方解釋不清楚,到最後,還是發佈到stack overflow,通過大佬解惑才弄明白,這裏我再整理一下。

我的思路

逐個解析一下討論到的案例:

沒有特別說明的,運行環境就是是chrome77。
  var a
  if(true) {
    a = 5
    function a() {}
    a = 0
    console.log(a)
  }

  console.log(a)

先整理一下我的思路:

  • 首先,全局有一個變量a聲明;
  • 然後,有一個if語句塊,是一個塊級作用域;
  • 塊級作用域裏面有變量a的賦值,還聲明瞭一個函數a

按照聲明提升原則,函數a聲明會被提升,這裏沒有使用letconst,所以我按ES5之前的聲明提升規則去分析的。

沒有塊級作用域,提升到全局,而且函數聲明優先於變量聲明,所以會覆蓋變量a的聲明。

那麼答案是:

0
0

很遺憾,這個答案是錯的,至少現代瀏覽器是錯的(我後來在IE的模擬環境運行,確實是這個結果)。

現在,公佈一下正確答案

0
5

這個答案非常讓人迷惑不解:

  • 第一個輸出0,好理解
  • 第二個輸出5,到底是怎麼回事?

    • 如果理解成:let a = function(){},那麼a=5是暫時性死區;
    • 如果理解成:var a = function(){},那麼會和上面一樣,輸出0 0

大佬的解析

先看看代碼運行解析[引用1],我們可以把代碼運行看成這樣:

var a¹;
 if (true) {
   function a²() {} // hoisted
   a² = 5;
   a¹ = a²; // at the location of the declaration, the variable leaves the block      
   a² = 0;
  console.log(a²)
}
console.log(a¹);

解釋一下:

  • 全局的變量a聲明,這裏沒有問題。
  • 重點1,塊級域的函數a聲明提升,提升到塊級域頂部
  • 重點2a=5,這裏賦值的對象是本地的函數a,覆蓋了。
  • 重點中的重點,執行到函數a聲明處,本地的變量a覆蓋了全局的變量a。按照[引用2]解釋,這裏也是提升,也就是說函數a聲明提升了兩次。到這裏疑惑終於解開了!

總結

這裏要說明兩點:

  • 塊級作用域{}內的函數聲明,在ES5中並沒有嚴格規定,也就是這塊依賴於實現[引用3]。
  • 在項目裏面,不要在塊級作用域{}內聲明函數,要在全局作用域或者函數作用域的頂層聲明函數。

引用參考

  1. [confused about function declaration in { }

](https://stackoverflow.com/que...

  1. [What are the precise semantics of block-level functions in ES6?

](https://stackoverflow.com/que...

  1. web legacy compatibility semantics
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章