幾張動(dòng)圖帶你回顧JS的變量提升
大家好,我是TianTian。
今天要分享的內(nèi)容是JS中的變量提升(Hoisting)。
前幾天,一個(gè)大二的學(xué)妹拿來一道題跑來問我,這道題目不簡單, 思來想去的發(fā)現(xiàn)里面正是有變量提升。
關(guān)于變量提升的文章,太多了,之所以拿出來說,推薦的原因在于:
通過幾張動(dòng)圖的形式,就把這個(gè)過程描述清楚了,太有趣了。
精力有限,圖片并非本人制作,如有侵權(quán),會(huì)刪除滴~
Hoisting的定義
首先,看看mdn對(duì)它的解讀:
變量提升(Hoisting)被認(rèn)為是, Javascript中執(zhí)行上下文 (特別是創(chuàng)建和執(zhí)行階段)工作方式的一種認(rèn)識(shí)。在 ECMAScript? 2015 Language Specification 之前的JavaScript文檔中找不到變量提升(Hoisting)這個(gè)詞。不過,需要注意的是,開始時(shí),這個(gè)概念可能比較難理解,甚至惱人。
我們可以理解成,在編譯的階段,js引擎幫我們把變量和函數(shù)的聲明放在最前面,但實(shí)際上變量和函數(shù)聲明在代碼里的位置是不會(huì)動(dòng)的。
知道了個(gè)大概后,我們從流程上來說說吧。
創(chuàng)建階段
當(dāng)JS引擎得到我們的腳本時(shí),它做的第一件事就是為我們代碼中的數(shù)據(jù)設(shè)置內(nèi)存。在這一點(diǎn)上沒有執(zhí)行任何代碼,它只是在為執(zhí)行準(zhǔn)備一切。函數(shù)聲明和變量的存儲(chǔ)方式是不同的。函數(shù)是以對(duì)整個(gè)函數(shù)的引用來存儲(chǔ)的。

我們可以看到圖中的內(nèi)容,但是如果這個(gè)時(shí)候,遇到了變量會(huì)怎么樣呢,尤其是ES6中的const和let,讓我們接著往下看:

從圖中我們可以總結(jié)出來,用let,const聲明的變量以uninitialized來存儲(chǔ)的。
這里就解釋了,為什么我們用let和const聲明的,不能在它之前使用,也就是暫時(shí)性死區(qū)。
那用var關(guān)鍵字來聲明的話,結(jié)果你猜到了嘛,我們來看圖:

很顯然,我們發(fā)現(xiàn),用var關(guān)鍵字聲明的變量是以默認(rèn)值undefined來存儲(chǔ)的。
這里是平時(shí)面試會(huì)被問到的一個(gè)考點(diǎn),現(xiàn)在看來不過如此。
小結(jié)一下,他們的區(qū)別:
用var關(guān)鍵字聲明的變量是以默認(rèn)值undefined來存儲(chǔ)的。 用let,const聲明的變量以uninitialized來存儲(chǔ)的。
現(xiàn)在,創(chuàng)建階段已經(jīng)完成,我們可以實(shí)際執(zhí)行代碼了。
讓我們看看,如果我們?cè)诼暶骱瘮?shù)或任何變量之前,在文件頂部有3個(gè)console.log語句,會(huì)發(fā)生什么。
執(zhí)行階段
由于函數(shù)是以對(duì)整個(gè)函數(shù)代碼的引用來存儲(chǔ)的,我們甚至可以在創(chuàng)建函數(shù)的那一行之前調(diào)用它們,我們來看看結(jié)果是怎么樣的:

我們發(fā)現(xiàn),輸出的內(nèi)容是5,也就是能夠正常的運(yùn)行,那么遇到var聲明的變量呢,我們來看圖:

我們從結(jié)果中可以看到,當(dāng)我們引用一個(gè)在其聲明前用var關(guān)鍵字聲明的變量時(shí),它將簡單地返回其存儲(chǔ)的默認(rèn)值:undefined。
這就是我們說的怪異行為,所以我們盡可能的不去使用它,讓代碼更加的規(guī)范。
這個(gè)時(shí)候,ES6中引出了const和let。
它的提出,就是為了防止意外地引用未定義的變量,就像我們用var關(guān)鍵字一樣,每當(dāng)我們?cè)噲D訪問未初始化的變量時(shí),我們希望它會(huì)拋出一個(gè)ReferenceError。

這也就是我們通常意義上所說的暫時(shí)性死區(qū),我們引用const定義的變量,在聲明前調(diào)用,就會(huì)出現(xiàn)Error。
接下來的步驟,就如我們看到的那樣子,當(dāng)引擎通過我們實(shí)際聲明變量的那一行時(shí),內(nèi)存中的值會(huì)被我們實(shí)際聲明的值所覆蓋,如下圖所示:

小結(jié)
看完整個(gè)流程后,是不是更加清晰這個(gè)過程啦~
做個(gè)小結(jié):
在我們執(zhí)行代碼之前,函數(shù)和變量被存儲(chǔ)在內(nèi)存中,以獲得一個(gè)執(zhí)行環(huán)境。這就是所謂的Hoisting。 函數(shù)是以對(duì)整個(gè)函數(shù)的引用來存儲(chǔ)的,帶有var關(guān)鍵字的變量的值是未定義的,帶有l(wèi)et和const關(guān)鍵字的變量是未初始化的。
最后
今天分享的內(nèi)容關(guān)于Hoisting(變量提升),或許對(duì)你有那么一點(diǎn)點(diǎn)幫助。
面試題交流群持續(xù)開放,已經(jīng)分享了近 許多 個(gè)面經(jīng)。
加我微信: DayDay2021,備注面試,拉你進(jìn)群~
我是 TianTian,我們下篇見~
往期推薦
