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

基本概念

  • 編譯器帮辟,解釋器
  • 抽象語(yǔ)法樹(shù)
  • 字節(jié)碼和機(jī)器碼

編譯器和解釋器

計(jì)算機(jī)不能直接理解高級(jí)語(yǔ)言,只能直接理解機(jī)器語(yǔ)言水由,所以必須要把高級(jí)語(yǔ)言翻譯成機(jī)器語(yǔ)言首昔,計(jì)算機(jī)才能執(zhí)行高級(jí)語(yǔ)言編寫(xiě)的程序寡喝。根據(jù)語(yǔ)言的執(zhí)行流程,可以把語(yǔ)言分成編譯型語(yǔ)言和解釋型語(yǔ)言勒奇。

編譯型語(yǔ)言:程序在執(zhí)行之前需要一個(gè)專門(mén)的編譯過(guò)程预鬓,把程序編譯成 為機(jī)器語(yǔ)言的文件,運(yùn)行時(shí)不需要重新翻譯赊颠,直接使用編譯的結(jié)果就行了格二。程序執(zhí)行效率高,依賴編譯器竣蹦,跨平臺(tái)性差些顶猜。如C、C++痘括、go等.

解釋型語(yǔ)言: 程序不需要編譯长窄,程序在運(yùn)行時(shí)才翻譯成機(jī)器語(yǔ)言(所以執(zhí)行前需要環(huán)境中安裝了解釋器)仙粱,每執(zhí)行一次都要翻譯一次忠聚。因此效率比較低。效率比較低弧关,依賴解釋器翰舌,跨平臺(tái)性好嚣潜。

編譯型與解釋型,兩者各有利弊椅贱, 不能一概而論懂算。前者由于程序執(zhí)行速度快,同等條件下對(duì)系統(tǒng)要求較低庇麦,因此像開(kāi)發(fā)操作系統(tǒng)计技、大型應(yīng)用程序、數(shù)據(jù)庫(kù)系統(tǒng)等時(shí)都采用它山橄,像C/C++垮媒、Pascal/Object Pascal(Delphi)等都是編譯語(yǔ)言,而一些網(wǎng)頁(yè)腳本驾胆、服務(wù)器腳本及輔助開(kāi)發(fā)接口這樣的對(duì)速度要求不高涣澡、對(duì)不同系統(tǒng)平臺(tái)間的兼容性有一定要求的程序則通常使用解釋性語(yǔ)言贱呐,如JavaScript丧诺、VBScript、Perl奄薇、Python驳阎、Ruby、MATLAB 等等。

編譯器和解釋器

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

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

抽象語(yǔ)法樹(shù)

抽象語(yǔ)法樹(shù)(Abstract Syntax Tree娶耍,AST),或簡(jiǎn)稱語(yǔ)法樹(shù)(Syntax tree)饼酿,是源代碼語(yǔ)法結(jié)構(gòu)的一種抽象表示榕酒。它以樹(shù)狀的形式表現(xiàn)編程語(yǔ)言的語(yǔ)法結(jié)構(gòu),樹(shù)上的每個(gè)節(jié)點(diǎn)都表示源代碼中的一種結(jié)構(gòu)故俐。之所以說(shuō)語(yǔ)法是“抽象”的想鹰,是因?yàn)檫@里的語(yǔ)法并不會(huì)表示出真實(shí)語(yǔ)法中出現(xiàn)的每個(gè)細(xì)節(jié)。比如购披,嵌套括號(hào)被隱含在樹(shù)的結(jié)構(gòu)中杖挣,并沒(méi)有以節(jié)點(diǎn)的形式呈現(xiàn);而類似于 if-condition-then 這樣的條件跳轉(zhuǎn)語(yǔ)句刚陡,可以使用帶有兩個(gè)分支的節(jié)點(diǎn)來(lái)表示惩妇。

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

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

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

JavaScript代碼執(zhí)行過(guò)程

  1. 生成AST(抽象語(yǔ)法樹(shù))
  2. 生成字節(jié)碼
  3. 執(zhí)行代碼

生成AST

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

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

通過(guò) javascript-ast 網(wǎng)站勃刨,可以大概了解 代碼生成的 Tokens 以及 AST大致的樣子波材。

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

例如let a = 2;,通常會(huì)被分解為下面這些詞法單元 let身隐、a廷区、=2贾铝、; 空格是否會(huì)被當(dāng)做詞法單元取決于空格在這門(mén)語(yǔ)言中是否會(huì)具有意義隙轻。

分解Token示意圖
  1. 語(yǔ)法分析:將詞法單元根據(jù)一定規(guī)則組裝成 AST
let a = 2;
console.log(a);

我們可以看到生成的AST結(jié)構(gòu)如下:

AST

高級(jí)語(yǔ)言是開(kāi)發(fā)者可以理解的語(yǔ)言埠帕,編譯器和解釋器理解不了。所以無(wú)論你使用的是解釋型語(yǔ)言還是編譯型語(yǔ)言玖绿,在編譯過(guò)程中敛瓷,它們都會(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編寫(xiě)規(guī)范的插件) 的檢測(cè)流程也是先將源碼轉(zhuǎn)換成 AST, 然后利用 AST 來(lái)檢查代碼規(guī)范的問(wèn)題

生成字節(jié)碼

JavaScript引擎通過(guò)解釋器來(lái)將 AST 轉(zhuǎn)換成字節(jié)碼苍姜,字節(jié)碼是無(wú)法直接執(zhí)行的牢酵,需要將其轉(zhuǎn)為機(jī)器碼才能直接執(zhí)行。V8早期的時(shí)候衙猪,是直接將AST轉(zhuǎn)成機(jī)器碼的馍乙,后來(lái)因?yàn)?V8 需要消耗大量的內(nèi)存來(lái)存放轉(zhuǎn)換后的機(jī)器碼,導(dǎo)致嚴(yán)重的內(nèi)存占用問(wèn)題垫释。為了解決這個(gè)問(wèn)題丝格,引入 了字節(jié)碼。字節(jié)碼是比機(jī)器碼輕量得多的代碼棵譬。

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

執(zhí)行代碼

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

監(jiān)聽(tīng)熱點(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)化操作趾唱,并保存下來(lái)涌乳。保存下來(lái)的機(jī)器碼的作用和緩存很類似,當(dāng)解釋器再次遇到相同的內(nèi)容時(shí)甜癞,就可以直接執(zhí)行保存下來(lái)的機(jī)器碼夕晓。

這樣代碼執(zhí)行得越久,執(zhí)行效率就會(huì)越快带欢,因?yàn)闀?huì)有越來(lái)越多的字節(jié)碼被標(biāo)記為 熱點(diǎn)代碼运授,遇到他們就可以直接執(zhí)行,而不用轉(zhuǎn)成機(jī)器碼乔煞。

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

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

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

總結(jié)

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

JavaScript代碼執(zhí)行過(guò)程

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市肋坚,隨后出現(xiàn)的幾起案子乡括,更是在濱河造成了極大的恐慌,老刑警劉巖智厌,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诲泌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡铣鹏,警方通過(guò)查閱死者的電腦和手機(jī)敷扫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诚卸,“玉大人葵第,你說(shuō)我怎么就攤上這事『夏纾” “怎么了羹幸?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辫愉。 經(jīng)常有香客問(wèn)我栅受,道長(zhǎng),這世上最難降的妖魔是什么恭朗? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任屏镊,我火速辦了婚禮,結(jié)果婚禮上痰腮,老公的妹妹穿的比我還像新娘而芥。我一直安慰自己,他們只是感情好膀值,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布棍丐。 她就那樣靜靜地躺著误辑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪歌逢。 梳的紋絲不亂的頭發(fā)上巾钉,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音秘案,去河邊找鬼砰苍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛阱高,可吹牛的內(nèi)容都是我干的赚导。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼赤惊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼吼旧!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起未舟,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤黍少,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后处面,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體厂置,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年魂角,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昵济。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡野揪,死狀恐怖访忿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斯稳,我是刑警寧澤海铆,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站挣惰,受9級(jí)特大地震影響卧斟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜憎茂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一珍语、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧竖幔,春花似錦板乙、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蛋铆。三九已至,卻和暖如春放接,著一層夾襖步出監(jiān)牢的瞬間刺啦,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工透乾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人磕秤。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓乳乌,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親市咆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子汉操,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348