昨天收到一封來(lái)自深圳的一位前端童鞋的郵件巍沙,郵件內(nèi)容如下(很抱歉葵姥,未經(jīng)過(guò)他的允許,公開郵件內(nèi)容句携,不過(guò)我相信其他人肯定也有同樣的問(wèn)題榔幸,所以,直接把問(wèn)題原文拋出來(lái)):
“讀了你的幾篇關(guān)于JS(變量對(duì)象矮嫉、作用域削咆、上下文、執(zhí)行代碼)的文章蠢笋,我個(gè)人覺(jué)得有點(diǎn)抽象拨齐,難以深刻理解。我想請(qǐng)教下通過(guò)什么途徑能夠深入點(diǎn)的了解javascript解析引擎在執(zhí)行代碼前后是怎么工作的昨寞,ecma英文版實(shí)在看不下去呵呵瞻惋∠寐耍”
其實(shí)這個(gè)問(wèn)題個(gè)人覺(jué)得太籠統(tǒng)了,直接回答很難回答歼狼,所以掏导,我打算先把他的問(wèn)題拆解成如下幾個(gè)子問(wèn)題,并對(duì)其表達(dá)個(gè)人的觀點(diǎn)羽峰,希望對(duì)有同樣困惑的童鞋能夠有所幫助趟咆。
1. 什么是JavaScript解析引擎?
簡(jiǎn)單地說(shuō)限寞,JavaScript解析引擎就是能夠“讀懂”JavaScript代碼忍啸,并準(zhǔn)確地給出代碼運(yùn)行結(jié)果的一段程序。比方說(shuō)履植,當(dāng)你寫了 var a = 1 + 1; 這樣一段代碼计雌,JavaScript引擎做的事情就是看懂(解析)你這段代碼,并且將a的值變?yōu)?玫霎。
學(xué)過(guò)編譯原理的人都知道凿滤,對(duì)于靜態(tài)語(yǔ)言來(lái)說(shuō)(如Java、C++庶近、C)翁脆,處理上述這些事情的叫編譯器(Compiler),相應(yīng)地對(duì)于JavaScript這樣的動(dòng)態(tài)語(yǔ)言則叫解釋器(Interpreter)鼻种。這兩者的區(qū)別用一句話來(lái)概括就是:編譯器是將源代碼編譯為另外一種代碼(比如機(jī)器碼反番,或者字節(jié)碼),而解釋器是直接解析并將代碼運(yùn)行結(jié)果輸出叉钥。 比方說(shuō)罢缸,firebug的console就是一個(gè)JavaScript的解釋器。
但是投队,現(xiàn)在很難去界定說(shuō)枫疆,JavaScript引擎它到底算是個(gè)解釋器還是個(gè)編譯器,因?yàn)榉笱唬热缦馰8(Chrome的JS引擎)息楔,它其實(shí)為了提高JS的運(yùn)行性能,在運(yùn)行之前會(huì)先將JS編譯為本地的機(jī)器碼(native machine code)扒披,然后再去執(zhí)行機(jī)器碼(這樣速度就快很多)值依,相信大家對(duì)JIT(Just In Time Compilation)一定不陌生吧。
我個(gè)人認(rèn)為碟案,不需要過(guò)分去強(qiáng)調(diào)JavaScript解析引擎到底是什么鳞滨,了解它究竟做了什么事情我個(gè)人認(rèn)為就可以了。對(duì)于編譯器或者解釋器究竟是如何看懂代碼的蟆淀,翻出大學(xué)編譯課的教材就可以了拯啦。
這里還要強(qiáng)調(diào)的就是,JavaScript引擎本身也是程序熔任,代碼編寫而成褒链。比如V8就是用C/C++寫的。
2. JavaScript解析引擎與ECMAScript是什么關(guān)系疑苔?
JavaScript引擎是一段程序甫匹,我們寫的JavaScript代碼也是程序,如何讓程序去讀懂程序呢惦费?這就需要定義規(guī)則兵迅。比如,之前提到的var a = 1 + 1;薪贫,它表示:
- 左邊var代表了這是申明(declaration),它申明了a這個(gè)變量
- 右邊的+表示要將1和1做加法
- 中間的等號(hào)表示了這是個(gè)賦值語(yǔ)句
- 最后的分號(hào)表示這句語(yǔ)句結(jié)束了
上述這些就是規(guī)則恍箭,有了它就等于有了衡量的標(biāo)準(zhǔn),JavaScript引擎就可以根據(jù)這個(gè)標(biāo)準(zhǔn)去解析JavaScript代碼了瞧省。那么這里的ECMAScript就是定義了這些規(guī)則扯夭。其中ECMAScript 262這份文檔,就是對(duì)JavaScript這門語(yǔ)言定義了一整套完整的標(biāo)準(zhǔn)鞍匾。其中包括:
- var交洗,if,else橡淑,break构拳,continue等是JavaScript的關(guān)鍵詞
- abstract,int梁棠,long等是JavaScript保留詞
- 怎么樣算是數(shù)字置森、怎么樣算是字符串等等
- 定義了操作符(+,-掰茶,>暇藏,<等)
- 定義了JavaScript的語(yǔ)法
- 定義了對(duì)表達(dá)式,語(yǔ)句等標(biāo)準(zhǔn)的處理算法濒蒋,比如遇到==該如何處理
- ??
標(biāo)準(zhǔn)的JavaScript引擎就會(huì)根據(jù)這套文檔去實(shí)現(xiàn)盐碱,注意這里強(qiáng)調(diào)了標(biāo)準(zhǔn),因?yàn)橐灿胁话凑諛?biāo)準(zhǔn)來(lái)實(shí)現(xiàn)的沪伙,比如IE的JS引擎瓮顽。這也是為什么JavaScript會(huì)有兼容性的問(wèn)題。至于為什么IE的JS引擎不按照標(biāo)準(zhǔn)來(lái)實(shí)現(xiàn)围橡,就要說(shuō)到瀏覽器大戰(zhàn)了暖混,這里就不贅述了,自行Google之翁授。
所以拣播,簡(jiǎn)單的說(shuō)晾咪,ECMAScript定義了語(yǔ)言的標(biāo)準(zhǔn),JavaScript引擎根據(jù)它來(lái)實(shí)現(xiàn)贮配,這就是兩者的關(guān)系谍倦。
3. JavaScript解析引擎與瀏覽器又是什么關(guān)系?
簡(jiǎn)單地說(shuō)泪勒,JavaScript引擎是瀏覽器的組成部分之一昼蛀。因?yàn)闉g覽器還要做很多別的事情,比如解析頁(yè)面圆存、渲染頁(yè)面叼旋、Cookie管理、歷史記錄等等沦辙。那么夫植,既然是組成部分,因此一般情況下JavaScript引擎都是瀏覽器開發(fā)商自行開發(fā)的怕轿。比如:IE9的Chakra偷崩、Firefox的TraceMonkey、Chrome的V8等等撞羽。
從而也看出阐斜,不同瀏覽器都采用了不同的JavaScript引擎。因此诀紊,我們只能說(shuō)要深入了解哪個(gè)JavaScript引擎谒出。
4. 深入了解其內(nèi)部原理的途徑有哪些?
搞清楚了前面三個(gè)問(wèn)題邻奠,那這個(gè)問(wèn)題就好回答了笤喳。個(gè)人認(rèn)為,主要途徑有如下幾種(依次由淺入深):
- 看講JavaScript引擎工作原理的書
這種方式最方便碌宴,不過(guò)我個(gè)人了解到的這樣的書幾乎沒(méi)有杀狡,但是Dmitry A.Soshnikov博客上的文章真的是非常的贊,建議直接看英文贰镣,實(shí)在英文看起來(lái)吃力的呜象,可以看我翻譯的譯本 - 看ECMAScript的標(biāo)準(zhǔn)文檔
這種方式相對(duì)直接,原汁原味碑隆,因?yàn)橐婢褪歉鶕?jù)標(biāo)準(zhǔn)來(lái)實(shí)現(xiàn)的恭陡。目前來(lái)說(shuō),可以看第五版和第三版上煤,不過(guò)要看懂也是不容易的休玩。 - 看JS引擎源代碼
這種方式最直接,當(dāng)然也最難了。因?yàn)檫€牽涉到了如何實(shí)現(xiàn)詞法分析器拴疤,語(yǔ)法分析器等等更加底層的東西了永部,而且并非所有的引擎代碼都是開源的。
5. 以上幾種方式中第一種都很難看明白怎么辦呐矾?
其實(shí)第一種方式中的文章扬舒,作者已經(jīng)將文檔中內(nèi)容提煉出來(lái),用通俗易懂的方式闡述出來(lái)了凫佛。如果,看起來(lái)還覺(jué)得吃力孕惜,那說(shuō)明還缺少兩塊的東西:
- 對(duì)JavaScript本身還理解的不夠深入
如果你剛剛接觸JavaScript愧薛,或者說(shuō)以前甚至都沒(méi)有接觸過(guò)。那一下子就想要去理解內(nèi)部工作原理衫画,的確是很吃力的毫炉。首先應(yīng)該多看看書,多實(shí)踐實(shí)踐削罩,從知識(shí)和實(shí)踐的方式來(lái)了解JavaScript預(yù)言特性瞄勾。這種情況下,你只需要了解現(xiàn)象弥激。比方說(shuō)进陡,(function(){})() 這樣可以直接調(diào)用該匿名函數(shù)、用閉包可以解決循環(huán)中的延遲操作的變量值獲取問(wèn)題等等微服。要了解這些趾疚,都是需要多汲取和實(shí)踐的。實(shí)踐這里就不多說(shuō)了以蕴,而知識(shí)汲取方面可以多看看書和博客糙麦。這個(gè)層面的書就相對(duì)比較多了,《Professional JavaScript for Web Developers》就是本很好的書(中文版請(qǐng)自行尋找)丛肮。 - 缺乏相應(yīng)的領(lǐng)域知識(shí)
當(dāng)JavaScript也達(dá)到一定深度了赡磅,但是,還是看不大明白宝与,或者沒(méi)法很深入到內(nèi)部去一探究竟焚廊。那就意味著缺少對(duì)應(yīng)的領(lǐng)域知識(shí)。這里明顯的就是編譯原理相關(guān)的知識(shí)伴鳖。不過(guò)节值,其實(shí)對(duì)這塊了解個(gè)大概基本看起來(lái)就沒(méi)問(wèn)題了。要再繼續(xù)深入榜聂,那需要對(duì)編譯原理了解的很深入搞疗,比如說(shuō)詞法分析采用什么算法,一般怎么處理。會(huì)有什么問(wèn)題匿乃,如何解決桩皿,AST生成算法一般有哪幾種等等。那要看編譯原理方面的書幢炸,也有基本經(jīng)典的書泄隔,比如《Compilers: Principles, Techniques, and Tools》這本也是傳說(shuō)中的龍書,還有非常著名的《SICP》和《PLAI》宛徊。不過(guò)其實(shí)根據(jù)個(gè)人經(jīng)驗(yàn)佛嬉,對(duì)于Dmitry的文章,要看懂它闸天,只要你對(duì)JavaScript有一定深度的了解暖呕,同時(shí)你大學(xué)計(jì)算機(jī)的課程都能大致掌握了(尤其是操作系統(tǒng)),也就是說(shuō)基礎(chǔ)不錯(cuò)苞氮,理解起來(lái)應(yīng)該沒(méi)問(wèn)題湾揽。因?yàn)檫@些文章基本沒(méi)有涉及底層編譯相關(guān)的,只是在解釋文檔的內(nèi)容笼吟,并且其中很多東西都是相通的库物,比如:context的切換與CPU的進(jìn)程切換、函數(shù)相關(guān)的的局部變量的棧存儲(chǔ)贷帮、函數(shù)退出的操作等等都是一致的戚揭。
以上就是個(gè)人對(duì)這個(gè)問(wèn)題的看法,除此之外皿桑,我覺(jué)得毫目,學(xué)習(xí)任何技術(shù)都不能操之過(guò)急,要把基礎(chǔ)打扎實(shí)了诲侮,這樣學(xué)什么都會(huì)很快镀虐。