js變量提升原理

前端的小伙伴大概都知道,js中的var變量存在變量提升阵幸,在es6以后隨著let變量的出現(xiàn)辕翰,變量提升問(wèn)題得以解決缴挖。那么變量提升的原理是什么洪唐?es6又是怎么解決變量提升問(wèn)題的蒿柳?下面我們來(lái)共同探尋答案:

我們首先來(lái)了解幾個(gè)概念客扎,執(zhí)行上下文饺律、變量環(huán)境胧瓜、詞法環(huán)境矢棚。(本文不涉及閉包、this指向等問(wèn)題)

執(zhí)行上下文

當(dāng)一段js代碼被執(zhí)行時(shí)府喳,js引擎會(huì)先對(duì)其進(jìn)行編譯蒲肋,并創(chuàng)建執(zhí)行上下文。執(zhí)行上下文分為三種:全局執(zhí)行上下文钝满、函數(shù)執(zhí)行上下文兜粘、eval執(zhí)行上下文

  1. 全局執(zhí)行上下文
    js執(zhí)行全局代碼時(shí),js引擎會(huì)創(chuàng)建一個(gè)全局的執(zhí)行上下文弯蚜,全局執(zhí)行上下文在頁(yè)面的生命周期內(nèi)只有一份孔轴。即每個(gè)js文件,只有一個(gè)全局上下文碎捺。

  2. 函數(shù)執(zhí)行上下文
    當(dāng)執(zhí)行一個(gè)js函數(shù)時(shí)路鹰,js引擎會(huì)創(chuàng)建一個(gè)函數(shù)執(zhí)行上下文,當(dāng)函數(shù)執(zhí)行結(jié)束之后收厨,函數(shù)的執(zhí)行上下文會(huì)被銷(xiāo)毀晋柱。一個(gè)函數(shù)被多次調(diào)用,會(huì)創(chuàng)建多個(gè)執(zhí)行上下文诵叁。

  3. eval執(zhí)行上下文
    使用eval函數(shù)執(zhí)行一段js代碼時(shí)雁竞,會(huì)創(chuàng)建一個(gè)eval的執(zhí)行上下文。

當(dāng)js文件執(zhí)行時(shí)黎休,首先會(huì)創(chuàng)建全局執(zhí)行上下文浓领,并壓入調(diào)用棧玉凯,當(dāng)調(diào)用js函數(shù)時(shí),會(huì)創(chuàng)建函數(shù)執(zhí)行上下文联贩,并壓入調(diào)用棧漫仆。當(dāng)函數(shù)執(zhí)行完之后,函數(shù)執(zhí)行上下文便會(huì)從棧中移出泪幌。如以下代碼的執(zhí)行:

var a = "123"
function func1() {
    var b = "123"
    console.log(b)
    func2()
}
funcgion func2() {
    const c = "456"
    console.log(c)
}
func1()
執(zhí)行上下文

執(zhí)行上下文中其實(shí)還包含了另外兩個(gè)對(duì)象盲厌,一個(gè)變量環(huán)境對(duì)象和一個(gè)詞法環(huán)境對(duì)象。那么接下來(lái)我們來(lái)看一下什么是變量環(huán)境和詞法環(huán)境

變量環(huán)境

變量環(huán)境存在于執(zhí)行上下文中祸泪,其本質(zhì)是一個(gè)對(duì)象吗浩,變量環(huán)境中存儲(chǔ)的是此作用域內(nèi)定義的變量、函數(shù)信息等信息没隘。如全局執(zhí)行上下文中的變量環(huán)境存儲(chǔ)的是全局的變量和函數(shù)信息懂扼。函數(shù)執(zhí)行上下文中的變量環(huán)境則存放的是函數(shù)的參數(shù)、局部變量等信息右蒲。

其實(shí)阀湿,js的代碼在執(zhí)行前還有一個(gè)編譯的過(guò)程,在編譯過(guò)程中瑰妄,var變量和function函數(shù)部分會(huì)被js引擎放入到變量環(huán)境中陷嘴,并且變量會(huì)被默認(rèn)設(shè)置為undefined。在執(zhí)行階段间坐,js引擎會(huì)在變量環(huán)境中查找聲明的變量和函數(shù)灾挨。這就是我們所說(shuō)的“變量提升”,這也是為什么函數(shù)可以在函數(shù)的實(shí)現(xiàn)之前調(diào)用竹宋。

例:

console.log(a)
var a = "123"
function func1() {
    console.log(a)
}
func1()

以上代碼的執(zhí)行順序是:

  • js引擎先進(jìn)行編譯劳澄,并把a變量和func1放入到變量環(huán)境中,并把a變量設(shè)置為undefined

  • 進(jìn)入執(zhí)行階段蜈七,執(zhí)行第一行代碼console.log(a)浴骂,此時(shí)從變量環(huán)境中取出a的值為undefined,所以打印結(jié)果為undefined宪潮。

  • 執(zhí)行第二行代碼var a = "123"溯警,將變量環(huán)境中的a變量賦值為字符串123

  • 執(zhí)行最后一行代碼func1()狡相,js引擎從變量環(huán)境中找出對(duì)應(yīng)的func1梯轻,并執(zhí)行里面的代碼console.log(a),打印結(jié)果為123

所以以上代碼輸出結(jié)果為

undefined
123

雖然在a聲明之前打印a變量尽棕,但是卻并沒(méi)有報(bào)錯(cuò)喳挑。

詞法環(huán)境

ES6之前,js中只支持全局作用域和函數(shù)作用域,并不支持塊級(jí)作用域伊诵。ES6之后单绑,js引入了letconst關(guān)鍵字,從而解決了變量提升問(wèn)題并使js支持了塊級(jí)作用域曹宴。

其實(shí)說(shuō)letconst沒(méi)有變量提升并不準(zhǔn)確搂橙,當(dāng)js代碼被編譯時(shí),letconst變量代碼會(huì)被存放在詞法環(huán)境中笛坦。此時(shí)letconst變量已經(jīng)被提升了区转,但是只是創(chuàng)建被提升,初始化和賦值并沒(méi)有被提升版扩,如果在賦值之前去讀寫(xiě)該變量废离,便會(huì)報(bào)錯(cuò),這就是我們所說(shuō)的“暫時(shí)性死區(qū)”礁芦。

那實(shí)現(xiàn)塊級(jí)作用域的原理是什么呢蜻韭?其實(shí)在詞法環(huán)境中,維護(hù)了一個(gè)作用域棧柿扣,棧底是函數(shù)的最外層變量(letconst聲明的變量)湘捎,進(jìn)入一個(gè)作用域塊后,就會(huì)把該作用域中的變量入棧窄刘;當(dāng)作用域中的代碼執(zhí)行完成之后,該作用域的信息就會(huì)從棧頂彈出舷胜。
我們舉個(gè)以下例子來(lái)說(shuō)明

例:

function fun()
{
    let a = 1
    {
        let a = 2
        let b = 3
        console.log(a)
        console.log(b)
    }
    console.log(a)
    console.log(b)
}
fun()

如圖:


fun函數(shù)執(zhí)行狀態(tài)
  1. 當(dāng)fun函數(shù)被編譯時(shí)娩践,外層的a變量首先被創(chuàng)建,并存放至詞法環(huán)境作用域棧中烹骨,此時(shí)函數(shù)內(nèi)部的塊級(jí)作用域中的變量不會(huì)被創(chuàng)建翻伺。
  2. 當(dāng)函數(shù)執(zhí)行至作用域塊時(shí),let alet b也被創(chuàng)建并入棧存放至棧頂沮焕。并將a賦值為2吨岭,將賦值為3
  3. 當(dāng)執(zhí)行至console.log(a)console.log(b)時(shí)峦树,js引擎首先從棧頂找到ab的值并打印出23辣辫。
  4. 當(dāng)作用域塊執(zhí)行完成之后,作用域塊中的變量信息從棧中彈出魁巩。
  5. 接著執(zhí)行console.log(a)找到的是棧底的a變量急灭,并打印出1。接著執(zhí)行console.log(b)谷遂,由于在詞法環(huán)境和變量環(huán)境中都找不到b變量葬馋,所以便會(huì)報(bào)錯(cuò)b is not defined

如果同一個(gè)函數(shù)中不同作用域存在相同的變量(如上面例子的a),那么變量的查找順序是怎樣的呢畴嘶?

  1. 首先在詞法環(huán)境作用域棧的棧頂?shù)淖兞啃畔⒅虚_(kāi)始查找
  2. 如果找到該變量蛋逾,則直接返回該變量在此作用域塊中的值,如果沒(méi)有找到則從棧頂往下依次查找窗悯。
  3. 如果從詞法環(huán)境中的棧頂?shù)綏5锥紱](méi)有找到区匣,則從變量環(huán)境中查找。

總結(jié):

講到這里蟀瞧,我想應(yīng)該可以回答一下文章開(kāi)始所提的兩個(gè)問(wèn)題:

變量提升的原理是什么沉颂?
在js代碼編譯階段,var變量和function函數(shù)會(huì)被js引擎放入到變量環(huán)境中悦污,并且var變量會(huì)被默認(rèn)設(shè)置為undefined铸屉。需要注意的是,var變量只有創(chuàng)建和初始化被提升切端,賦值并沒(méi)有被提升彻坛;而function的創(chuàng)建、初始化和賦值均會(huì)被提升踏枣。所以在變量的聲明之前昌屉,該變量的值是undefined,而函數(shù)則可以在聲明之前正常調(diào)用茵瀑。

let间驮、const是怎么解決變量提升問(wèn)題的?
在js代碼編譯階段,letconst變量會(huì)被js引擎放入到詞法環(huán)境中马昨。與var一樣竞帽,letconst變量也被提升了。但只是創(chuàng)建被提升鸿捧,變量的初始化和賦值并未被提升屹篓,如果在賦值之前讀寫(xiě)該變量,就會(huì)形成暫時(shí)性死區(qū)匙奴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末堆巧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子泼菌,更是在濱河造成了極大的恐慌谍肤,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哗伯,死亡現(xiàn)場(chǎng)離奇詭異谣沸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)笋颤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)乳附,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)内地,“玉大人,你說(shuō)我怎么就攤上這事赋除≮寤海” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵举农,是天一觀的道長(zhǎng)荆针。 經(jīng)常有香客問(wèn)我,道長(zhǎng)颁糟,這世上最難降的妖魔是什么航背? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮棱貌,結(jié)果婚禮上玖媚,老公的妹妹穿的比我還像新娘。我一直安慰自己婚脱,他們只是感情好今魔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著障贸,像睡著了一般错森。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上篮洁,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天涩维,我揣著相機(jī)與錄音,去河邊找鬼袁波。 笑死瓦阐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锋叨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼宛篇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼娃磺!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起叫倍,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤偷卧,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后吆倦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體听诸,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蚕泽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晌梨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桥嗤。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖仔蝌,靈堂內(nèi)的尸體忽然破棺而出泛领,到底是詐尸還是另有隱情,我是刑警寧澤敛惊,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布渊鞋,位于F島的核電站,受9級(jí)特大地震影響瞧挤,放射性物質(zhì)發(fā)生泄漏锡宋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一特恬、第九天 我趴在偏房一處隱蔽的房頂上張望执俩。 院中可真熱鬧,春花似錦鸵鸥、人聲如沸奠滑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宋税。三九已至,卻和暖如春讼油,著一層夾襖步出監(jiān)牢的瞬間杰赛,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工矮台, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乏屯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓瘦赫,卻偏偏與公主長(zhǎng)得像辰晕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子确虱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 這是我第23篇簡(jiǎn)書(shū)含友。 為什么簡(jiǎn)書(shū)沒(méi)有目錄功能啊,要不是在這這么久了都想轉(zhuǎn)去某金了校辩。窘问。 本章內(nèi)容: 1、執(zhí)行上下文2...
    東西里閱讀 1,987評(píng)論 2 14
  • 之前對(duì)這塊的認(rèn)識(shí)宜咒,只限于變量提升惠赫、作用域、es6新增了故黑,let儿咱、const等新的聲明方式庭砍,他們是塊級(jí)作用域,con...
    不摸魚(yú)閱讀 236評(píng)論 0 1
  • 一概疆、作用域: 1.變量的作用域 作用域是變量的可作用范圍逗威,變量只有在自己的作用域下才會(huì)生效。 函數(shù)會(huì)產(chǎn)生作用域岔冀,在...
    小男孩兒長(zhǎng)大了閱讀 247評(píng)論 0 0
  • var與function 變量提升:在當(dāng)前上下文中(全局/私有/塊級(jí))凯旭,JS代碼自上而下執(zhí)行之前,瀏覽器會(huì)提前處理...
    羽晞yose閱讀 383評(píng)論 0 0
  • (注1:如果有問(wèn)題歡迎留言探討使套,一起學(xué)習(xí)罐呼!轉(zhuǎn)載請(qǐng)注明出處,喜歡可以點(diǎn)個(gè)贊哦U旄摺)(注2:更多內(nèi)容請(qǐng)查看我的目錄嫉柴。) ...
    love丁酥酥閱讀 1,564評(píng)論 2 3