前段時間有人在知乎上邀請我回答一個問題:為什麼最後一個 a 是 1 不是 5?
題目如下:
console.log(a)
if (true) {
a = 1
function a() {}
a = 5
console.log(a)
}
console.log(a)
我的第一反應是:undefined,5,5。估計和題主想的一樣
分析一波
假設沒有 if(true),即如下程式碼:
console.log(a)
a = 1
function a() {}
a = 5
console.log(a)
console.log(a)
那麼答案什麼?
a()、5、5
這解釋了兩個特性
- 變數、函式提升且函式的權重大於變數
- 在 a 沒有用 var 宣告時,
a=XX
預設是用 var 來宣告
變數、函式提升方面的知識點在於:
變數會提升,函式也會提升,並且函式提升的優先順序大於變數,如下例:
console.log(a)
console.log(a())
var a = 1
function a() {
console.log(2)
}
console.log(a)
var a = 3
a = 4
console.log(a)
console.log(a())
a()、2、1、4、a is not a function
回過頭來看這道題目
console.log(a)
if (true) {
a = 1
function a() {}
a = 5
console.log(a)
}
console.log(a)
if (ture) {} ,形成了作用域,鎖住了這片變數,function a(){}
無法逃逸。換句話說,只有 {}
塊級識別符號在,function a() {}
就被所在塊級作用域中,也就說在 if (ture) {}
這片塊級作用域下,它不會提升到全域性頂層,而是在 if(true){}
下,即程式碼執行時是這樣:
console.log(a)
if (true) {
+function a() {};
a = 1;
-function a() {};
a = 5;
console.log(a);
}
console.log(a)
如果你在 a = 1
前列印 a,a 的值就是 function a(){}
所以這道題全域性環境下,沒有變數提升,寫在第一行的 console.log(a)
因為找不到 a,所以值為 undefined
進入 if(true) {}
中,function a(){}
函式提升,且權重最高,所以賦值之前的塊級作用域中的 a 為 function a() {}
,window.a
為 undefined
程式碼執行到 function a() {}
後,塊級作用域中的 a 還是為 1,但是全域性變數 a 被賦值為 1
執行到 a = 5
,傳統賦值,影響的是塊級作用域中的 a,而不會影響全域性變數 a,所以列印的第二個 console.log(a)
為 5,第三個 console.log(a)
為 1
那麼問題來了,為什麼一執行 function a(){}
,全域性變數 a 就被賦值為 1?
我陷入的沉思,後來在回答中發現雲補斷山回答了,說是
歷史原因,為了相容之前的 ES5 的語法,所以在規範規定了塊級作用域內函式宣告的一些行為,各個瀏覽器實現可能不一樣
簡單來說,在塊級作用域內的函式函式宣告,行為類似於 var ,都會在全域性作用域宣告一個同名變數(也就是 window 上掛一個同名的屬性,預設值是 undefined),因為 ES6 遇到塊級作用域,會基於塊級作用域建立 environment record,存放當前塊級作用域內的變數,所以這個函式宣告會提升到塊級作用域頂部(而非全域性作用域頂部)
我們學的 JavaScript 是 ECMAScript,但是我們把程式碼執行在瀏覽器上時就要按照瀏覽器的標準,瀏覽器裡會有一些私貨在,最經典的是 __proto__
,倒逼 ECMA 採納。話說回來,按照這位仁兄的意思
// 因為 function a() 宣告過,所以全域性有個 window.a
console.log(a)
if (true) {
// 宣告歸宣告,但是函式提升提升與作用域相關,所以提升至此塊級作用域頂部
a = 1
// 塊級作用域中的 a 被賦值為 1
function a() {}
// 原地爆炸,執行函式後,全域性 window.a 被賦值為塊級作用域中的 a
a = 5
// 塊級作用域中的 a 又被賦值為 5
console.log(a)
}
console.log(a)
最詭異的是執行 function a() {}
後,全域性 window.a 被賦值且為塊級作用域中的 a
這個事情沒完!!
等等,我就說的玩玩的,如果工作中或面試中真遇到這類問題,我也許還是不會解。
太詭異了,這不是考題範圍(塊級作用域、函式提升、變數提升)
就這樣先吧
本文參與了 SegmentFault 思否徵文「如何“反殺”面試官?」,歡迎正在閱讀的你也加入。