Chrome瀏覽器引擎 Blink & V8
{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這篇文章是我的前端技術系列文章中瀏覽器工作原理欄目中的第二篇。瀏覽器引擎(也被稱作佈局引擎或渲染引擎)是瀏覽器的重要組成部分。瀏覽器引擎最重要的工作就是將HTML文本和其他頁面中的資源轉換成可以與用戶產生交互的頁面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了瀏覽器引擎外,佈局引擎和渲染引擎是另外兩個相關的概念,理論上,兩個引擎可以獨立實現,但在實際情況中,往往很少將二者分開實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了包含佈局和渲染引擎外,瀏覽器引擎還遵循"},{"type":"link","attrs":{"href":"https://en.wikipedia.org/wiki/Content_Security_Policy","title":null},"content":[{"type":"text","text":"文檔安全策略(Content Security Policy)"}]},{"type":"text","text":"以保證站點間相互獨立。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在運行JavaScript代碼的功能上,基本上主流的瀏覽器都使用獨立的引擎,起初JavaScript語言只被用於在瀏覽器中使用,但現在JavaScript幾乎可以在任何地方使用,這需要JavaScript引擎可以獨立於瀏覽器單獨使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而像"},{"type":"link","attrs":{"href":"https://en.wikipedia.org/wiki/Electron_(software_framework)","title":null},"content":[{"type":"text","text":"Electron framework"}]},{"type":"text","text":"這樣的技術就是整合Chromium的渲染引擎和Nodejs而實現的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我想通過這篇文章把V8中的技術儘可能的詳述,涵蓋的內容會比較多,可反覆閱讀 :)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"常見的瀏覽器引擎"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瀏覽器引擎是Web平臺技術中一系列標準(HTML、CSS、ECMAScript、WebGL、Web Storage等等)的具體實現,不同的瀏覽器引擎在遵循同樣的標準下,還實現了額外的功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Gecko是Mozilla的瀏覽器引擎,在Firefox中使用,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#333333","name":"user"}}],"text":"SpiderMonkey是Firefox的JavaScript引擎。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Apple爲Safari瀏覽器創造了Webkit引擎,Webkit引擎內置了"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#333333","name":"user"}}],"text":"JavaScriptCore引擎。雖然Apple允許在IOS設備上可以使用其他的瀏覽器代替Safari,但所用通過App Store分發的瀏覽器必須使用Webkit引擎。例如,Opera Mini瀏覽器在IOS設備上使用Webkit引擎,而在其他設備上使用Blink引擎。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Google起初使用Webkit作爲Chrome瀏覽器的引擎,後來以Webkit引擎爲基礎創造了Blink引擎,所有基於Chromium開源瀏覽器衍生的產品都使用blink引擎。而大名鼎鼎的V8引擎就是Chromium-based瀏覽器的JavaScript引擎。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Microsoft維護着自己的EdgeHTML引擎,作爲老的Trident引擎的替代方案。新的Edge的瀏覽器已經開始使用Chromium的Blink引擎了,而EdgeHTML引擎只在window 10上的"},{"type":"link","attrs":{"href":"https://en.wikipedia.org/wiki/Universal_Windows_Platform","title":null},"content":[{"type":"text","text":"Universal Windows Platform"}]},{"type":"text","text":"中被使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c2/c2d5a491c0b230edf39337130cd12f67.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"天下合久必分,分久必合,隨着Edge也加入了Blink的陣營,基本上Webkit內核及Webkit內核的衍生Blink已經統治了瀏覽器市場。到目前,單單Chrome的市場佔有率已有六成。接下來,就讓我們來聊聊Blink和V8引擎。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"Chromium & Blink"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"寬泛的說,Blink實現了在瀏覽器頁籤中所有的渲染工作,其中包括:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實現了Web平臺中的標準,例如HTML標準,包括DOM、CSS等。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內置了V8引擎用於運行JavaScript。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從網絡堆棧中獲取資源"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"構建DOM樹"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"計算樣式和佈局"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內置了"},{"type":"link","attrs":{"href":"https://chromium.googlesource.com/chromium/src/+/HEAD/cc/README.md","title":null},"content":[{"type":"text","text":"Chrome Compositor"}],"marks":[{"type":"underline"}]},{"type":"text","text":"和繪製圖形的能力"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"藉助"},{"type":"link","attrs":{"href":"https://chromium.googlesource.com/chromium/src/+/HEAD/content/public/README.md","title":null},"content":[{"type":"text","text":"Content public APIs"}],"marks":[{"type":"underline"}]},{"type":"text","text":",Blink可以被內置在很多諸如Chromium,Android WebView和Opera這樣的應用中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"進程/線程架構"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"進程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Chromium擁有一套多進程架構。Chromium有一個瀏覽器進程和多個帶有沙盒能力的渲染進程。Blink則運行在渲染進程中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從安全的角度考慮,讓不同的站點保持相互隔離是非常重要的,這被稱作"},{"type":"text","marks":[{"type":"strong"}],"text":"站點隔離(Site Isolation)"},{"type":"text","text":"。理論上講,一個渲染進程應該最多隻能負責一個站點的渲染工作。但實際上,當用戶打開很多頁籤時,渲染進程與站點1對1的關係會佔用大量的內存。所以一個渲染進程可能會被多個iframe或頁籤所共享,也就是說一個頁面中的多個iframe可能被多個渲染進程渲染,而在不同頁面中的多個iframe也可能被同一個渲染進程渲染。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"所以,在iframe,頁籤和渲染進程間並不存在一對一的關係。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於Blink運行在渲染進程中的沙盒中,當Blink需要訪問文件或播放視頻或者訪問用戶信息(cookie、password等)時必須與瀏覽器進程通信。這種不同進程間的通信方式被"},{"type":"link","attrs":{"href":"https://chromium.googlesource.com/chromium/src/+/master/mojo/README.md","title":null},"content":[{"type":"text","marks":[{"type":"underline"}],"text":"Mojo"}]},{"type":"text","text":"實現。隨着Chromium不斷向服務化架構演進,Blink可以通過Mojo來降低消息傳遞過程中對發送方和接收方對於具體實現的依賴(服務可能在多個進程中,也可能在同一個進程中,消息傳遞方式不同)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/24/240541c94b8227856e0c4062a8f9db7e.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Mojo"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Mojo是一系列庫的集合,用於提供一種進程內或跨進程的通信方案,其中包含了與平臺無關的通用的IPC方案、消息IDL格式化和可以與不同語言集成的綁定庫。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d9/d990029007862acd8e71392f766b48ee.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Message pipe"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個"},{"type":"text","marks":[{"type":"strong"}],"text":"消息通道(Message pipe)"},{"type":"text","text":"建立其兩個"},{"type":"text","marks":[{"type":"strong"}],"text":"端點(endpoint)"},{"type":"text","text":"之間的通道。每一個端點都有一個用於收消息的隊列,同時還可以向另一個端點發送消息,而消息通道是雙向的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Mojom"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Mojom文件描述了消息的類型。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了消息通道和消息類型,通道中的一個端點可以被指定成"},{"type":"codeinline","content":[{"type":"text","text":"Remote"}],"marks":[{"type":"strong"}]},{"type":"text","text":",它可以發送Mojom文件中定義好類型的消息。另一個端點則可以被指定成"},{"type":"codeinline","content":[{"type":"text","text":"Receiver"}],"marks":[{"type":"strong"}]},{"type":"text","text":",用於接收消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"線程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Blink包含一個主線程,多個Worker線程,還有一些其他的線程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"幾乎所有重要的工作都運行在主線程上。包括運行JavaScript(除了Workers),DOM生成,CSS樣式和佈局計算等,所以交互性能的優化關鍵主要圍繞主線程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Blink會爲Web workers,Service workers創建出獨立的線程。雖然運行的都是JavaScript,但主線程與worker線程的運行環境是不共享的,需要通過消息來傳遞數據。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Blink和V8也可能會創建出其他的用於音視頻,數據庫和垃圾回收(GC)等功能的線程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於線程間通信,會使用PostTask提供的api。除了真的因爲性能的原因,使用共享內存的方式實現通信並不被推薦,這也是Blink不使用線程鎖(MutexLocks)的原因。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/89/89ab6c79bbfa58c540f5ebc72be974b5.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"Page, Frame, Document, DOMWindow"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"概念"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個"},{"type":"text","marks":[{"type":"strong"}],"text":"頁面(Page)"},{"type":"text","text":"代表一個瀏覽器頁籤,一個渲染進程可能負責渲染多個頁面。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個"},{"type":"text","marks":[{"type":"strong"}],"text":"框(Frame)"},{"type":"text","text":"代表主框或者一個iframe,一個"},{"type":"text","marks":[{"type":"strong"}],"text":"頁面至少包含一個框。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個"},{"type":"text","marks":[{"type":"strong"}],"text":"DOMWindow"},{"type":"text","text":"代表JavaScript中的window對象,每個框只有一個"},{"type":"text","marks":[{"type":"strong"}],"text":"DOMWindow"},{"type":"text","text":"。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個"},{"type":"text","marks":[{"type":"strong"}],"text":"Document"},{"type":"text","text":"代表JavaScript中的window.document對象,每個框只有一個"},{"type":"text","marks":[{"type":"strong"}],"text":"Document。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個"},{"type":"text","marks":[{"type":"strong"}],"text":"ExecutionContext"},{"type":"text","text":"在主線程中抽象一個Document,在worker線程中抽象WorkerGlobalScope。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"渲染進程 :頁面 = 1 :N"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"頁面 :框 = 1 :M"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Frame : DOMWindow : Document (或ExecutionContext) 在任何情況下都是 1 : 1 : 1 ,但有時引用關係會變化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"跨進程iframes(OOPIF)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然站點隔離機制讓頁面變的更安全,但卻增加了複雜度。站點隔離致力於爲每一個站點創建一個渲染進程,例"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"如,"},{"type":"link","attrs":{"href":"https://mail.example.com","title":null},"content":[{"type":"text","marks":[{"type":"underline"}],"text":"https://mail.example.com"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":" 和 "},{"type":"link","attrs":{"href":"https://chat.example.com","title":null},"content":[{"type":"text","marks":[{"type":"underline"}],"text":"https://chat.example.com"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":" 屬於同一個站點,而 "},{"type":"link","attrs":{"href":"https://noodles.com","title":null},"content":[{"type":"text","marks":[{"type":"underline"}],"text":"https://noodles.com"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":" 和 "},{"type":"link","attrs":{"href":"https://pumpkins.com","title":null},"content":[{"type":"text","text":"https://pumpkins.com"}],"marks":[{"type":"underline"}]},{"type":"text","text":"則不屬於同一個站點。如果一個頁面中存在跨站點的iframe則可能被多個渲染進程承載。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從主框的角度看,主框是"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"LocalFrame,iframe則是RemoteFrame。從iframe的角度看,主框則是RemoteFrame,而iframe則是LocalFrame。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"LocalFrame和RemoteFrame間的通信被瀏覽器進程管理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Web IDL綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Web IDL (Web Interface definition language)是用於描述Web平臺中定義的標準如何被瀏覽器實現的接口定義語言,通過瀏覽器對這些標準中定義的接口的實現,Web開發者可以使用JavaScript對象來調用這些標準功能。Blink在實現這些標準的同時,還需要爲V8中的JavaScript提供調用Blink的途徑,這就是Web IDL Bindings。通過對Web IDL的實現和Bindings的存在,就實現了類似在JavaScript中訪問某個節點的第一個子節點"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"的功能(node."},{"type":"text","text":"firstChild)"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"。在實現了通用標準的同時,瀏覽器還實現了自己特有的功能定義,通用的標準被定義在"},{"type":"link","attrs":{"href":"https://heycam.github.io/webidl/","title":null},"content":[{"type":"text","text":"the Web IDL spec"}],"marks":[{"type":"underline"}]},{"type":"text","text":",而Blink自己的定義則被定義在"},{"type":"link","attrs":{"href":"https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/bindings/IDLExtendedAttributes.md","title":null},"content":[{"type":"text","text":"Blink-specific IDL extended attributes"}],"marks":[{"type":"underline"}]},{"type":"text","text":"中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常在idl文件被構建時,"},{"type":"link","attrs":{"href":"https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/bindings/IDLCompiler.md","title":null},"content":[{"type":"text","marks":[{"type":"underline"}],"text":"the IDL compiler"}]},{"type":"text","text":" 會自動爲具體的實現類生成Blink-V8的綁定。當在JavaScript中調用node.firstChild時,V8會調用V8Node::firstChildAttributeGetterCallback() ,然後進一步調用Node::firstChild() 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"渲染流水線"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Rendering pipeline定義了從HTML字符到在屏幕上顯示像素的過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/59/59f93c4bc67cf902dcaad2524f5d709f.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於這部分的內容可以閱讀前一篇文章。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"V8"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"V8是Google打造的開源的,高性能的JavaScript和WebAssembly引擎,使用C++語言實現。V8引擎被應用在Chrome、Nodejs和其他應用中。V8引擎可以獨立運行,也可以運行在任何的C++程序中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個V8的實例被稱作Isolate,每一個isolate都有獨立GC的堆棧空間。這就意味着一個Isolate中的JavaScript對象不能直接訪問另一個Isolate中的對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Chrome中,每個渲染進程都有一個V8 Isolate,所有被同一個渲染進程處理的站點的JavaScript代碼在同一個Isolate中運行。但對於Web worker,每一個worker則擁有自己的Isolate。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Isolate中,存在一個或多個JavaScript上下文環境(JavaScript content)。Chrome爲每個iframe創建一個JavaScript環境。此外,每個Chrome extension對於一個iframe都有自己的JavaScript環境。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Blink通常使用ScriptState對象作爲JavaScript環境的引用,blink::ScriptState與v8::Context有着1 : 1的關係。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"執行流水線"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JavaScript腳本的運行需要經歷一系列的過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a5/a5595e9e68069bbe5d22c63c9ab8b324.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要運行的JavaScript腳本會從網絡或緩存中被"},{"type":"text","marks":[{"type":"strong"}],"text":"加載。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過對JavaScript腳本文本的分析可以生成用於描述源代碼結構化的數據,"},{"type":"text","marks":[{"type":"strong"}],"text":"抽象語法樹(AST)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}},{"type":"strong"}],"text":"Ignition解釋器"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"會將AST轉化成生成體積更小的"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}},{"type":"strong"}],"text":"字節碼"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":",字節碼中的每行指令代表着對寄存器的操作,當字節碼生後以後AST將會被廢棄以節省空間,後續的執行和優化都基於字節碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"在解釋器執行字節碼時,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}},{"type":"strong"}],"text":"Object Shapes"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"會試圖將代碼中對象的類型緩存下來生成"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}},{"type":"strong"}],"text":"Type Feedback"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":",當訪問這些對象時會嘗試從緩存中獲取,如果找不到再動態查找並更新緩存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}},{"type":"strong"}],"text":"TurboFan"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"是V8中的代碼優化編譯器,它會評估函數是否需要被進一步優化成機器碼以提高性能,需要被優化的函數被編譯成"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}},{"type":"strong"}],"text":"Optimized Code"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"但當編譯後的函數被發現函數中變量的數據類型與之前緩存的類型不同時,則需要放棄優化的代碼回到字節碼重新解釋執行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來讓我們逐一瞭解每個過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"加載(Loading)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"加載是V8獲取JavaScript腳本文本的過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/33/33980d9f58ed08dcbca52989379c4b24.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"V8並不負責資源的下載,所以這些資源可能來自網絡、緩存,也可能來自Service worker。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"V8可以在文件下載的同時進行接下來的分析工作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"V8擁有腳本熱加載的能力:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Warm Load"},{"type":"text","text":":當V8再次運行同樣的腳本時,會將腳本編譯後的結果緩存在硬盤中。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Hot Load:"},{"type":"text","text":"當第三次訪問時,V8可以跳過分析和編譯過程直接從硬盤中讀取之前被編譯的結果。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"分析(Parsing)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4f/4f9f1c010a79037cd568d8145b75bfd9.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分析是將JavaScript腳本文本轉化成"},{"type":"text","marks":[{"type":"strong"}],"text":"抽象語法樹(Abstract Syntax Tree)的過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"詞法分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"詞法分析(lexical analysis)"},{"type":"text","text":"是將一系列字符轉換成"},{"type":"text","marks":[{"type":"strong"}],"text":"標記(token)"},{"type":"text","text":"的過程。這裏的"},{"type":"text","marks":[{"type":"strong"}],"text":"標記"},{"type":"text","text":"是表示源代碼的最小單位,將輸入的字符流轉換成標記的過程被稱爲"},{"type":"text","marks":[{"type":"strong"}],"text":"標記化(tokenization)"},{"type":"text","text":",在這個過程中,詞法分析器還會對這些標記進行分類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"常見的標記分類有:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"標識符(identifier):x,color,UP"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關鍵字(keyword):if,var,return"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分隔符(separator):(,}, ;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"操作符(operator):*,=,>"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"字面量(literal):\"Hello world\", true, 666"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"註釋(comment):// 單行, /* 多行 */"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"Scanner"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"掃描器處在詞法分析的第一個階段,通常基於狀態機實現,可以在能識別的標記間不斷切換。每種標記可以代表一個字符或由多個字符組成的序列。很多時候,根據第一個非空字符就可以推斷出標記的類型,然後逐個處理後面的字符,直到出現不屬於該類型標記字符集中的字符時結束(最長一致性原則)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"掃描器只處理utf-16的字符集,所在在掃描器拿到字符之前會有字符集轉換的過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"Evaluator"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"評估器處在詞法分析的第二個階段,用於將某些帶有語義的詞定義成"},{"type":"text","marks":[{"type":"strong"}],"text":"值(value)"},{"type":"text","text":",一個"},{"type":"text","marks":[{"type":"strong"}],"text":"語義("},{"type":"text","marks":[{"type":"color","attrs":{"color":"#202122","name":"user"}},{"type":"strong"}],"text":"lexeme"},{"type":"text","marks":[{"type":"strong"}],"text":")"},{"type":"text","text":"是標記類型和值的組合。有的標記有值,比如標識符類型的標記,而有的標記沒有值,比如分隔符類型的標記。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如,在源代碼中是這樣的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"net_worth_future = (assets – liabilities);"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生成的語義標記可能是這樣的:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"IDENTIFIER net_worth_future\nEQUALS\nOPEN_PARENTHESIS\nIDENTIFIER assets\nMINUS\nIDENTIFIER liabilities\nCLOSE_PARENTHESIS\nSEMICOLON"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"語法分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"語法分析("},{"type":"text","marks":[{"type":"color","attrs":{"color":"#202122","name":"user"}},{"type":"strong"}],"text":"syntactic analysis,也叫 parsing"},{"type":"text","marks":[{"type":"strong"}],"text":")"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#202122","name":"user"}}],"text":"是根據某種給定的"},{"type":"text","text":"形式文法"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#202122","name":"user"}}],"text":"對由單詞序列(如英語單詞序列)構成的輸入文本進行分析並確定其語法結構的一種過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"語法分析器"},{"type":"text","text":"(parser)的作用是進行語法檢查、並根據輸入的單詞序列生成帶有層次的數據結構(通常是語法分析樹、抽象語法樹等)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"抽象語法樹與作用域"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"抽象語法樹(Abstract Syntax Tree)"},{"type":"text","text":"是源代碼結構的一種抽象表示。它以樹狀的形式表現編程語言的語法結構,樹上的每個節點都表示源代碼中的一種結構。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏我們以一段代碼作爲例子"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"function sayHi () {\n var str = \"hello world\";\n return str;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"轉化成AST後的結構是這樣的"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7e/7e62d86a0d4c4277bc91ad76a679f88d.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}},{"type":"strong"}],"text":"FunctionLiteral"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"代表sayHi函數,Block代表函數體。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"左邊的"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}},{"type":"strong"}],"text":"VariableDeclaration"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"代表變量str的聲明,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#323232","name":"user"}},{"type":"strong"}],"text":"Assignment"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#323232","name":"user"}}],"text":"代表賦值,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#323232","name":"user"}},{"type":"strong"}],"text":"ReturnStatement"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#323232","name":"user"}}],"text":"代表return語句。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏的"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#323232","name":"user"}},{"type":"strong"}],"text":"VariableProxy"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#323232","name":"user"}}],"text":"代表對變量的引用,在目前這個階段還不知道是引用哪個變量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在分析這個階段除了要生成AST,還要分析作用域。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1a/1ac03cdfbd40f995afbd50da9ee3b5a8.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過分析,全局作用域中包含函數sayHi的聲明,而sayHi的函數作用域中包含變量str的聲明。而兩個"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#323232","name":"user"}},{"type":"strong"}],"text":"VariableProxy"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#323232","name":"user"}}],"text":"所引用的變量也可以確定下來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#202122","name":"user"}}],"text":"V8有兩種分析器:Preparser和Full parser。Preparser分析器可以推遲那些不是立即需要分析的函數以減少代碼啓動需要的時間。Preparser只會處理語法分析和一些錯誤的檢查而不會生成抽象語法樹。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"V8在這個階段對各種類型的標記的掃描有着各種的優化手段,感興趣的同學可以繼續閱讀"},{"type":"link","attrs":{"href":"https://v8.dev/blog/scanner","title":null},"content":[{"type":"text","text":"Blazingly fast parsing, part 1: optimizing the scanner"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這裏推薦一個用於分析抽象語法樹的網站:"},{"type":"link","attrs":{"href":"https://astexplorer.net/","title":null},"content":[{"type":"text","text":"AST Explorer"}]},{"type":"text","text":"。在各種lint自定義規則,babel、webpack插件等代碼分析、生成的場景裏都有幫助。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"解釋(I"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"nterpreting"},{"type":"text","text":")"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解釋階段會將AST轉換成字節碼(bytecode)。得益於"},{"type":"link","attrs":{"href":"https://en.wikipedia.org/wiki/Just-in-time_compilation","title":null},"content":[{"type":"text","text":"即時編譯(just-in-time (JIT) compilation)"}]},{"type":"text","text":"技術,包括V8在內的現代瀏覽器JavaScript引擎結合了"},{"type":"text","marks":[{"type":"strong"}],"text":"提前編譯(AOT)"},{"type":"text","text":"的高性能和解釋的靈活性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"起初,代碼首先被編譯器快速的編譯成沒有被優化過的機器碼,在運行的同時再有選擇的將需要優化的代碼通過更高級的編譯器進行優化再編譯。這樣做雖然提高了運行速度,但也浪費了資源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中比較顯著的問題是被編譯過的機器碼會佔用大量的內存,即使有的代碼可能只會被執行一次。爲了解決這些問題,V8團隊提出了新的JavaScript解釋器,"},{"type":"link","attrs":{"href":"https://v8.dev/docs/ignition","title":null},"content":[{"type":"text","text":"Ignition"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"藉助"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}},{"type":"strong"}],"text":"Ignition"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":",V8可以將AST先轉化成更簡潔的字節碼,其大小與以往的機器碼相比縮小到50%至25%的空間。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/35/355cb34cfd38811ba0c6e19ab7e7025c.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}},{"type":"strong"}],"text":"Ignition"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"user"}}],"text":"生成的"},{"type":"text","text":"字節碼會被用於優化的編譯器和調試工具當作數據源,當字節碼生成以後,抽象語法樹就可以被廢棄掉以節省內存。在生成字節碼的同時,還會在字節碼上增加一些元數據,比如源代碼的位置和用於執行字節碼的處理函數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來我們利用d8近距離觀察一下bytecode,這裏我們將下面的JavaScript放在名爲bytecode.js的文件中,並運行d8的調試工具。"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"function sayHi () {\n var str = 'hello world'\n return str\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以得到下面的結果"}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"# d8 --print-bytecode bytecode.js\n[generated bytecode for function: (0x300d082d25e5 )]\nParameter count 1\nRegister count 2\nFrame size 16\n 0x300d082d26ae @ 0 : 12 00 LdaConstant [0]\n 0x300d082d26b0 @ 2 : 26 fa Star r0\n 0x300d082d26b2 @ 4 : 27 fe f9 Mov , r1\n 0x300d082d26b5 @ 7 : 62 3e 01 fa 02 CallRuntime [DeclareGlobals], r0-r1\n 0x300d082d26ba @ 12 : 0d LdaUndefined \n 0x300d082d26bb @ 13 : ab Return \nConstant pool (size = 1)\n0x300d082d2681: [FixedArray] in OldSpace\n - map: 0x300d08042201
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.