主題
昨天我的一個技術交流羣裏發了一段代碼,涉及的是變量和函數的聲明提升,執行結果非常讓人迷惑,大家討論許久,還是有地方解釋不清楚,到最後,還是發佈到stack overflow
,通過大佬解惑才弄明白,這裏我再整理一下。
我的思路
逐個解析一下討論到的案例:
沒有特別說明的,運行環境就是是chrome77。
var a
if(true) {
a = 5
function a() {}
a = 0
console.log(a)
}
console.log(a)
先整理一下我的思路:
- 首先,全局有一個
變量a
聲明; - 然後,有一個
if語句塊
,是一個塊級作用域; - 塊級作用域裏面有
變量a
的賦值,還聲明瞭一個函數a
。
按照聲明提升原則,函數a
聲明會被提升,這裏沒有使用let
、const
,所以我按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
聲明提升,提升到塊級域頂部。 -
重點2,
a=5
,這裏賦值的對象是本地的函數a
,覆蓋了。 -
重點中的重點,執行到
函數a
聲明處,本地的變量a
覆蓋了全局的變量a
。按照[引用2]解釋,這裏也是提升,也就是說函數a
聲明提升了兩次。到這裏疑惑終於解開了!
總結
這裏要說明兩點:
- 塊級作用域{}內的函數聲明,在
ES5
中並沒有嚴格規定,也就是這塊依賴於實現[引用3]。 - 在項目裏面,不要在塊級作用域{}內聲明函數,要在全局作用域或者函數作用域的頂層聲明函數。
引用參考
- [confused about function declaration in { }
](https://stackoverflow.com/que...
- [What are the precise semantics of block-level functions in ES6?