JavaScript代碼是如何被執(zhí)行的

基本概念

編譯器驯遇,解釋器
抽象語法樹
字節(jié)碼和機(jī)器碼

編譯器和解釋器

計(jì)算機(jī)不能直接理解高級(jí)語言怨规,只能直接理解機(jī)器語言羡疗,所以必須要把高級(jí)語言翻譯成機(jī)器語言挺物,計(jì)算機(jī)才能執(zhí)行高級(jí)語言編寫的程序。根據(jù)語言的執(zhí)行流程纺棺,可以把語言分成編譯型語言和解釋型語言榄笙。

編譯型語言:程序在執(zhí)行之前需要一個(gè)專門的編譯過程,把程序編譯成 為機(jī)器語言的文件祷蝌,運(yùn)行時(shí)不需要重新翻譯办斑,直接使用編譯的結(jié)果就行了。程序執(zhí)行效率高,依賴編譯器乡翅,跨平臺(tái)性差些。如C罪郊、C++蠕蚜、go等.

解釋型語言: 程序不需要編譯,程序在運(yùn)行時(shí)才翻譯成機(jī)器語言(所以執(zhí)行前需要環(huán)境中安裝了解釋器)悔橄,每執(zhí)行一次都要翻譯一次靶累。因此效率比較低。效率比較低癣疟,依賴解釋器挣柬,跨平臺(tái)性好。

編譯型與解釋型睛挚,兩者各有利弊邪蛔, 不能一概而論。前者由于程序執(zhí)行速度快扎狱,同等條件下對(duì)系統(tǒng)要求較低侧到,因此像開發(fā)操作系統(tǒng)、大型應(yīng)用程序淤击、數(shù)據(jù)庫系統(tǒng)等時(shí)都采用它匠抗,像C/C++、Pascal/Object Pascal(Delphi)等都是編譯語言污抬,而一些網(wǎng)頁腳本汞贸、服務(wù)器腳本及輔助開發(fā)接口這樣的對(duì)速度要求不高、對(duì)不同系統(tǒng)平臺(tái)間的兼容性有一定要求的程序則通常使用解釋性語言印机,如JavaScript矢腻、VBScript、Perl耳贬、Python踏堡、Ruby、MATLAB 等等咒劲。

我們都知道 JavaScript 存在變量提升顷蟆,在函數(shù)作用域內(nèi)的任何變量的聲明都會(huì)被提升到頂部并且值為 undefined。
所以JS引擎好像對(duì)同一個(gè)腳本執(zhí)行了兩次腐魂,第一次完成所有聲明帐偎,然后第二次才執(zhí)行代碼?還是先編譯整個(gè)代碼然后運(yùn)行它蛔屹?這兩種都不對(duì)削樊。

其實(shí)變量聲明不過只執(zhí)行上下文的小把戲。在執(zhí)行任何語句之前,解釋器就要從創(chuàng)建執(zhí)行上下文后已經(jīng)存在的作用域中找到變量的值漫贞。

抽象語法樹

抽象語法樹(Abstract Syntax Tree甸箱,AST),或簡(jiǎn)稱語法樹(Syntax tree)迅脐,是源代碼語法結(jié)構(gòu)的一種抽象表示芍殖。它以樹狀的形式表現(xiàn)編程語言的語法結(jié)構(gòu),樹上的每個(gè)節(jié)點(diǎn)都表示源代碼中的一種結(jié)構(gòu)谴蔑。之所以說語法是“抽象”的豌骏,是因?yàn)檫@里的語法并不會(huì)表示出真實(shí)語法中出現(xiàn)的每個(gè)細(xì)節(jié)。比如隐锭,嵌套括號(hào)被隱含在樹的結(jié)構(gòu)中窃躲,并沒有以節(jié)點(diǎn)的形式呈現(xiàn);而類似于 if-condition-then 這樣的條件跳轉(zhuǎn)語句钦睡,可以使用帶有兩個(gè)分支的節(jié)點(diǎn)來表示蒂窒。

字節(jié)碼和機(jī)器碼

字節(jié)碼(Byte-code):是一種包含執(zhí)行程序、由一序列 op 代碼/數(shù)據(jù)對(duì)組成的二進(jìn)制文件赎婚。字節(jié)碼是一種中間碼刘绣,它比機(jī)器碼更抽象。

機(jī)器碼 (Machine-code):計(jì)算機(jī)直接使用的程序語言挣输,其語句就是機(jī)器指令碼纬凤,機(jī)器指令碼是用于指揮計(jì)算機(jī)應(yīng)做的操作和操作數(shù)地址的一組二進(jìn)制數(shù)。

JavaScript代碼執(zhí)行過程

生成AST(抽象語法樹)
生成字節(jié)碼
執(zhí)行代碼

生成AST

生成AST的步驟可以拆分成以下兩個(gè)小步驟:

  1. 詞法分析:將JavaScript代碼解析成一個(gè)個(gè)詞法單元(token)
  2. 語法分析:將詞法單元根據(jù)一定規(guī)則組裝成抽象語法樹

通過 javascript-ast 網(wǎng)站撩嚼,可以大概了解 代碼生成的 Tokens 以及 AST大致的樣子停士。

  1. 詞法分析:將JavaScript代碼解析成一個(gè)個(gè)詞法單元(token)

例如let a = 2;,通常會(huì)被分解為下面這些詞法單元 let完丽、a恋技、=2逻族、; 空格是否會(huì)被當(dāng)做詞法單元取決于空格在這門語言中是否會(huì)具有意義蜻底。

語法分析:將詞法單元根據(jù)一定規(guī)則組裝成 AST
let a = 2;
console.log(a);
我們可以看到生成的AST結(jié)構(gòu)如下:

高級(jí)語言是開發(fā)者可以理解的語言,編譯器和解釋器理解不了聘鳞。所以無論你使用的是解釋型語言還是編譯型語言薄辅,在編譯過程中,它們都會(huì)生成一個(gè) AST抠璃。當(dāng)生成 AST之后站楚,編譯器/解析器后續(xù)的工作都要依靠 AST而不是源碼。

AST是一個(gè)非常重要數(shù)據(jù)結(jié)構(gòu)搏嗡,比如Babel的工作原理就是: ES6 的代碼解析成 AST -> 將 ES6 的 AST 轉(zhuǎn)換成 ES5 的AST -> 將 ES5的 AST 轉(zhuǎn)成 ES5的代碼窿春。Babel的相關(guān)文章推薦 深入淺出 Babel 上篇:架構(gòu)和原理 + 實(shí)戰(zhàn)拉一;我們使用的 Eslint(檢查JavaScript編寫規(guī)范的插件) 的檢測(cè)流程也是先將源碼轉(zhuǎn)換成 AST, 然后利用 AST 來檢查代碼規(guī)范的問題

生成字節(jié)碼

JavaScript引擎通過解釋器來將 AST 轉(zhuǎn)換成字節(jié)碼旧乞,字節(jié)碼是無法直接執(zhí)行的蔚润,需要將其轉(zhuǎn)為機(jī)器碼才能直接執(zhí)行。V8早期的時(shí)候良蛮,是直接將AST轉(zhuǎn)成機(jī)器碼的抽碌,后來因?yàn)?V8 需要消耗大量的內(nèi)存來存放轉(zhuǎn)換后的機(jī)器碼,導(dǎo)致嚴(yán)重的內(nèi)存占用問題决瞳。為了解決這個(gè)問題,引入 了字節(jié)碼左权。字節(jié)碼是比機(jī)器碼輕量得多的代碼皮胡。

字節(jié)碼是介于 AST 和機(jī)器碼之間的一種代碼。但是與特定類型的機(jī)器碼無關(guān)赏迟,字節(jié)碼需要通過解釋器將其轉(zhuǎn)換成機(jī)器碼后才能執(zhí)行屡贺。

執(zhí)行代碼

生成字節(jié)碼之后,就到了解釋和執(zhí)行字節(jié)碼階段了锌杀,

監(jiān)聽熱點(diǎn)代碼并優(yōu)化為二進(jìn)制機(jī)器碼

解釋器會(huì)逐條執(zhí)行字節(jié)碼甩栈,(解釋器除了負(fù)責(zé)生成字節(jié)碼,還會(huì)負(fù)責(zé)解釋執(zhí)行機(jī)器碼) 如果發(fā)現(xiàn)一段代碼重復(fù)執(zhí)行多次糕再,就會(huì)它記為熱點(diǎn)代碼(HotSpot)量没,V8會(huì)將這段熱點(diǎn)代碼提交給優(yōu)化編輯器,優(yōu)化編輯器會(huì)在后臺(tái)將字節(jié)碼編譯為二進(jìn)制代碼突想,然后在對(duì)編譯后的二進(jìn)制代碼執(zhí)行優(yōu)化操作殴蹄,并保存下來。保存下來的機(jī)器碼的作用和緩存很類似猾担,當(dāng)解釋器再次遇到相同的內(nèi)容時(shí)袭灯,就可以直接執(zhí)行保存下來的機(jī)器碼。

這樣代碼執(zhí)行得越久绑嘹,執(zhí)行效率就會(huì)越快稽荧,因?yàn)闀?huì)有越來越多的字節(jié)碼被標(biāo)記為 熱點(diǎn)代碼,遇到他們就可以直接執(zhí)行工腋,而不用轉(zhuǎn)成機(jī)器碼姨丈。

反優(yōu)化生成的二進(jìn)制機(jī)器碼

JavaScript是一種非常靈活的動(dòng)態(tài)語言,對(duì)象的結(jié)構(gòu)和屬性在運(yùn)行時(shí)任意被改變夷蚊,而經(jīng)過優(yōu)化后的代碼只能針對(duì)某種固定結(jié)構(gòu)构挤。一旦在執(zhí)行過程中,對(duì)象的結(jié)構(gòu)被動(dòng)態(tài)修改了惕鼓,那么優(yōu)化后的代碼會(huì)變成無效的代碼筋现,這時(shí)候優(yōu)化編輯器就需要執(zhí)行反優(yōu)化操作,經(jīng)過反優(yōu)化的代碼下次執(zhí)行時(shí)就會(huì)回退到解釋器解釋執(zhí)行。

字節(jié)碼的執(zhí)行是需要配合編譯器和解釋器的(這種技術(shù)稱為即時(shí)編譯 JIT)所以之前說 JS是一種解釋型語言并不準(zhǔn)確矾飞。

總結(jié)

整個(gè)過程如下面流程圖所示:


最后

獲取更多前端干貨和面試題一膨,加微信:


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市洒沦,隨后出現(xiàn)的幾起案子豹绪,更是在濱河造成了極大的恐慌,老刑警劉巖申眼,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞒津,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡括尸,警方通過查閱死者的電腦和手機(jī)巷蚪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來濒翻,“玉大人屁柏,你說我怎么就攤上這事∮兴停” “怎么了淌喻?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)雀摘。 經(jīng)常有香客問我裸删,道長(zhǎng),這世上最難降的妖魔是什么届宠? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任烁落,我火速辦了婚禮,結(jié)果婚禮上豌注,老公的妹妹穿的比我還像新娘伤塌。我一直安慰自己,他們只是感情好轧铁,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布每聪。 她就那樣靜靜地躺著,像睡著了一般齿风。 火紅的嫁衣襯著肌膚如雪药薯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天救斑,我揣著相機(jī)與錄音童本,去河邊找鬼。 笑死脸候,一個(gè)胖子當(dāng)著我的面吹牛穷娱,可吹牛的內(nèi)容都是我干的绑蔫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼泵额,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼配深!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嫁盲,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤篓叶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后羞秤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缸托,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年瘾蛋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嗦董。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瘦黑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奇唤,到底是詐尸還是另有隱情幸斥,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布咬扇,位于F島的核電站甲葬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏懈贺。R本人自食惡果不足惜经窖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梭灿。 院中可真熱鬧画侣,春花似錦、人聲如沸堡妒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皮迟。三九已至搬泥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伏尼,已是汗流浹背忿檩。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爆阶,地道東北人燥透。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓沙咏,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親兽掰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芭碍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359