ECMAScript在瀏覽器端運行被稱為JavaScript
在瀏覽器端JavaScript是一種描述型腳本語言,不需要編譯成中間語言,而是由瀏覽器按代碼塊順序進(jìn)行動態(tài)地解析與執(zhí)行。
關(guān)于作用域
ES6之前,只有全局作用域和函數(shù)作用域;ES6引入了塊級作用域。作用域是代碼執(zhí)行過程中的變量、函數(shù)或者對象的可訪問區(qū)域。
- 全局作用域:當(dāng)定義變量的地方?jīng)]有被 function 包括則是全局變量
- 函數(shù)作用域:在函數(shù)內(nèi)部定義的變量就是處于函數(shù)作用域中
- 塊狀作用域:類似于 if、switch 條件選擇或者 for、while 這樣的循環(huán)體即是所謂的塊級作用域
關(guān)于變量的生命周期
變量的生命周期包含著變量聲明、變量初始化、以及變量賦值三個步驟;其中聲明步驟會在作用域中注冊變量,初始化步驟負(fù)責(zé)為變量分配內(nèi)存并且創(chuàng)建作用域綁定,此時變量會被初始化為 undefined,最后的分配步驟則會將開發(fā)者指定的值分配給該變量。
關(guān)于預(yù)編譯
JavaScript代碼不需要編譯成一個其它可執(zhí)行的文件,但是js代碼在執(zhí)行之前會有一個準(zhǔn)備過程,這個準(zhǔn)備過程叫作預(yù)編譯。
預(yù)編譯的過程是不可見的,也不會產(chǎn)生額外的文件。
預(yù)編譯期間會主要完成兩件事情:
- 按順序掃碼代碼塊,如果發(fā)現(xiàn)語法錯誤,停止工作并報錯;
- 若沒有語法錯誤,做聲明變量和初始化變量的操作即所謂的變量提升操作;
先看一道簡單的題目
<script type="text/javascript">
console.log(a);
var a = 666;
console.log(a);
</script>
這道題目很簡單,但是卻很能說明問題。
提升操作
- 使用var聲明的變量的提升:把所有帶有var關(guān)鍵字的變量名,提升到當(dāng)前代碼塊的開始,并設(shè)初值為undefined。
- 函數(shù)提升:把所有用聲明式定義的函數(shù),提升到當(dāng)前代碼塊的開始,注意是做函數(shù)整體的提升。
具體細(xì)節(jié)如下
只提升加var的變量,沒有var不提升

如圖,沒有var,變量名沒有提升,相當(dāng)于沒有聲明變量a就直接使用它,會出現(xiàn)引用類型錯誤。
if結(jié)構(gòu)中的變量提升

如圖所示,變量提升一定是發(fā)生在預(yù)編譯期(提升之后才會進(jìn)入代碼的執(zhí)行期),而變量的提升與if條件語句是否成立無關(guān)。只要有var就會提升。
for循環(huán)中的變量提升

同樣會做變量提升。
函數(shù)內(nèi)部的變量提升

函數(shù)內(nèi)部定義的變量,不可能提上到函數(shù)的外部。如圖會直接報錯。

函數(shù)內(nèi)部的變量只會體升到函數(shù)的開始位置。
只提升聲明式定義的函數(shù)
定義函數(shù)有兩種方式:
- 聲明式:
function f(){} - 函數(shù)表達(dá)式:
var f = function(){}

函數(shù)提升是整個函數(shù)體提升,變量提升是提升的變量名。

如上圖所示,通過表達(dá)式定義的函數(shù)提升,報了一個TypeError的錯誤;是因為預(yù)編譯期將var關(guān)鍵字提升,也就是說這里的f就是一個普通變量,它的初值被賦為undefined;當(dāng)代碼執(zhí)行到第11行
f();時就相當(dāng)于是undefined();,所以肯定會報類型錯誤。
函數(shù)與變量同時提升

當(dāng)函數(shù)名與變量名同名,且同時提升,以函數(shù)為準(zhǔn)。
理由是函數(shù)是一等公民,地位高于變量。
提升不能跨script標(biāo)簽

開始那道題的答案

ES6中的變量提升以及暫時性死區(qū)
在 ES6 中以 let 與 const 關(guān)鍵字聲明的變量會在作用域頭部被初始化,不過這些變量僅允許在實際聲明之后使用。在作用域頭部與變量實際聲明處之間的區(qū)域就稱為所謂的暫時死區(qū)(Temporal Dead Zone),TDZ 能夠避免傳統(tǒng)的提升引發(fā)的潛在問題。
小結(jié)
以上,就是關(guān)于JavaScript變量提升的知識小結(jié)。