javascript宣告提升
前段時間閱讀一本書《ESCMScript 6入門》,其中一章關於”let和const命令”有一段程式碼
var tmp = new Date()
function f(){
console.log(tmp)
if(false){
var tmp = 'hello world'
}
}
關於變數宣告提升,思量許久,寫了以下理解。
這段程式碼看似簡單,其內部隱藏著重要的知識點“變數提升”這一現象。一般理解,呼叫f函式tmp要麼打印出日期,要麼打印出undefined,結果為後者。為何?
書本上僅僅這麼說明:原因在於變數提升,導致內層tmp變數覆蓋了外層tmp變數
僅僅理解這句話並不能清楚的知道為何?
再來看下面的程式碼:
function f(){
var item1 = 1;
var item2 = 'str';
var item = [1]
}
經過編譯器預處理類似變成:
function f(){
var item1,item2,item3;
item1 = 1;
item2 = 'str'
item3 = [1]
}
經過編譯器處理只會將所有的變數進行統一的宣告處理,所以,案例中經典的程式碼可以改為:
var tmp = new Date();
function f(){
var tmp;
console.log(tmp);
if(false){
var tmp = 'hello world';
}
}
總結:變數提升分為宣告提升和定義提升,賦值或者初始話並不會被提升,而程式碼中出現if條件語句,雖然程式碼不會進入那個片段,但是編譯器會預處理裡面的程式碼,將宣告tmp提升到f函式的最頂層,之所以覆蓋全域性,是因為內部已經聲明瞭tmp變數,將外部全域性變數對f函式的tmp的搜尋值遮蔽,因函式存在宣告,tmp變數就不再往上層去搜索值(原型鏈搜尋),這與物件的原型方法搜尋類似。
2018-09-06
上文是我在兩個月前寫的let變數提升的理解,但是自從面試之後,發現不僅僅那麼簡單,面試官這樣問我:var存在變數宣告提升,那let會麼?按照MDN文件的解釋,因為let存在暫時性死區,所以無法進行變數宣告提升。但真的是這樣簡單的理解了麼,下面我來把真正的原因講一下。
在MDN文件描述了let的知識,總結有一下三條:
- let宣告變數的作用是塊級的;
- let不能重複宣告一已經存在的變數;
- let存在暫時性死區,變數宣告不會被提升。
雖然你理解了上面三個總結,但是你的理解和對let的全面理解並不深刻。
let是否存在變數提升問題?
其實所有宣告變數的關鍵字,都存在變數宣告提升這個功能,只不過有些可以表現出這個特性,如var,有些不能,如let,為什麼let不能呢,暫時性死區又怎麼作用於它呢?
MDN文件在let上前後進行了兩次修改,所以其作者也是舉起不定,我們翻看es文件,會發現一段話:
The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts
。
這句話證明了let存在變數提升。提升不是一個技術名稱,js變數被宣告的過程:建立、初始化、賦值。
- var在建立和初始化的過程中都被提升;
- let在建立會被提升,而初始化沒用被提升,因為暫時性死區(變數在未使用之前,不能使用該變數);
- 函式建立、初始化和賦值都被提升; const只有建立和初始化過程,沒有賦值過程。
總結:
- let 具有變數提升過程,但不表現這個過程,因為存在暫時性死區;
- let 建立會被提升,但初始化不能被提升;
- 所有變數宣告的關鍵字操作都存在變數提升這個過程,只是因為不同情況和性質導致是否需要表現出來。
(文章結合自己思考和參考一些資料,如有侵權請聯絡本人,會盡快處理)。