函數提升的蜜汁行爲

衆所周知,JS裏,變量有變量提升,函數有函數提升。ES6之前沒有塊級作用域。
然而,當用函數聲明的方法定義function並遇到非ES6情況下的{}時,卻有一些蜜汁行爲。

一道題引發的慘案

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

理想狀態下:
if(){}裏不存在letconst。應該不會產生Block塊級作用域。
再根據函數提升,以上兩者相當於

    function a() {}
    a = 1
    console.log(a)
    console.log(a)

即輸出11

實際情況卻是:
前者11
後者1f a(){}

調試分析

debugger調試分析問題
先將問題簡化下

    debugger
    console.log(a)
    if (true) {
      debugger
      function a() {}
      debugger
    }
    console.log(a)

輸出undefinedf a(){}
第一個debugger,js開始執行時,全局聲明瞭a但未定義。
1.png

第二個debuggera的函數提升提升到塊級作用域Block上,而非全局。
2.png

第三個debugger,此處纔將Block內的a賦值給全局a
3.png

爲什麼是強調說是賦值而非直接在全局聲明呢。
現在將a=1補上即再看一遍過程即可說明

    debugger
    console.log(a)
    if (true) {
      debugger
      a = 1
      debugger
      function a() {}
      debugger
    }
    console.log(a)

第三個debuger時,1會覆蓋掉Block裏的f a(){}
4.png

第四個debuger時,執行到function的定義時,會發現全局a變成了1
5.png

最後這一步執行的前後,很明顯可以看出,當執行當函數聲明語句時,js會將塊級裏的函數名屬性值賦給全局的函數名屬性,即window.a = block.a
實際執行情況約等於如下

    var a;// 定義但未賦值,由function a(){}產生的。
    if (true) {
      let a = function a(){} // 函數提升到塊級
      a = 1
      window.a = a // 聲明語句時將塊級a賦值給全局a
      console.log(a)
    }
    console.log(a)

個人理解總結

結論僅個人從結果推導,非查閱文檔所得,所以未必準確。

(非es6情況下)當{}裏存在函數聲明(function a(){}這種類型)時,js執行情況如下:

  1. js開始執行,函數名全局聲明但未定義
  2. 進入函數聲明的{},產生臨時Block,並且function函數提升到Block
  3. 直到執行該function的定義語句時,纔會把Block裏該函數名的屬性賦值給全局(哪怕改變了該屬性甚至類型),舉例:window.a = a
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章