[WebKit] JavaScriptCore解析–基礎篇(四) 頁面解析與JavaScript元素的執行

來源:互聯網
上載者:User

很多地方都已經介紹了JavaScript在瀏覽器是如何被執行的,這裡介紹一下WebKit是如何?的。主要涉及JS的async,defer及普通指令碼的解析與執行過程的代碼實現。


1. 概要說明

先概要說明一下瀏覽器如何執行JavaScript的。 首先瀏覽器的頁面解析器(Document Parser)遇到<script>就會發起下載(指令碼內容在頁面內的就不用下載了)。然後針對不同情況執行的方式有所不同:

  . async (在script標籤中啟用了async屬性)  

    這是非同步執行,下載時不會阻塞Document Parser, 當JavaScript被載入完成後就會開始執行。

  . defer (在script標籤中啟用了defer屬性) 

    這個是延遲執行,下載時同樣不會阻塞Document Parser, 會放到Document Parser完成頁面解析後才會執行。

  . 對於引用外部的指令檔,下載時Document Parser會被阻塞至指令碼執行完。

  . 如果頁面中有Style Sheet還沒有解析成功, 則指令碼會被阻塞直到Style Sheet下載並解析完成。   


因為阻塞會嚴重影響頁面解析的效能,所以也是瀏覽器最佳化的重點:

  a. WebKit引入Preload Scanner允許在下載時嘗試繼續處理後面的資源。 [LINK] 不過隨著網路的提速,這個最佳化的效果應逐漸減弱。

  b. FireFox實現非同步HTML解析,WebKit也2013年年初實現了一個輕量級版本(只將tokenizer多線程化)。

  

2. WebCore中執行JavaScript 2.1 主要功能劃分

WebCore中關於JavaScript的元素的解析與執行,個人把相關類分成兩個主要的類別:

  頁面解析功能和JS執行功能。


  頁面解析類別的側重於在HTMLDocumentParser解析頁面過程中管理指令碼的建立、規劃執行策略等。這一部分細節功能比較複雜,在W3C中有專門的定義,WebKit中的實現很多地方也註上了所參考的章節。

  JS執行類別基於Document和Frame來管理JS執行環境,並同JSC協作執行JS指令碼。


下面是一個總覽, 黃色背景的類屬於為頁面解析類別,紅色背景的部分則歸到執行類別。

      

*主要類的代碼基本集中在bindings/js目錄下。

 2.2 JS元素的執行 

這裡的執行不包括頁面對JS執行的策略,主要針對具體的執行過程。

      

 JSMainThreadExecState  負責最終對接JSC模組執行JS指令碼。在系統中是一個單例的對象,也就是保證單進程與JSC協作。這同樣也是JS的一個限制點。

 ScriptController 維護一個JavaScriptCore執行環境,在必要時調用JSMainThreadExecState執行。

 ScriptElement  代表了一個具體的script元素,包括JavaScript, SVG等。


再附上一張時序圖來協助瞭解它們間的關係, (其中指令碼的執行決策是由HTMLScriptRunner來發起的):

   

2.3 JS元素的解析與管理

在WebKit內部,頁面解析與指令碼控制的操作主要是在HTMLDocumentParser,HTMLScriptRunner和ScriptElement中實現的。

     


2.3.1 JS解析

從下面這張經典的圖說起: 

這張圖源自W3C的官方定義, 與主文相關的是說明JS之所以會導致頁面解析阻塞正是因為它允許指令碼使用document.write()改變頁面內容,會造成DOM樹可能會需要重新解析(reparser)。所以非但瀏覽器要做些最佳化,JS開發人員也要注意JS的實現方法,比如編寫non-block
script。


另外不但JS阻塞Parser, Style Sheet也會阻塞JS。如果JS裡包含了對某個元素的顯示內容的檢測,在CSS還沒有載入解析完成的情況下,肯定得不到正確結果。所以對應在WebKit也對應出了不同的定義。


在HTMLScriptRunner有兩個重要的成員變數:

 m_scriptsToExecuteAfterParsing  -> 由requestDeferredScript()添加。

 m_parserBlockingScript
-> 由requestParsingBlockingScript添加。


根據名字也可以看出來前者是儲存defer指令碼,後者是儲存一般的指令碼的。至於async則是在下載完成後觸發的,稍後說明。

2.4 JS運行策略

之前說到不同的JS載入屬性的執行方法會不一樣:

    async -> 是由ScriptRunner::notifyScriptReady在指令碼下載完成時執行的。

    defer -> 是由HTMLDocumentParser::notifyFinished()在頁面解析完成時發起執行的。

    其它的JS指令碼則在處理token時由HTMLDocumentParser::canTakeNextToken來檢測並發起執行的。


2.4.1一般JS指令碼的執行

一般沒有帶async和defer屬性的JS指令碼,被稱為解析阻塞指令碼(Parsing Blocking Scripts),則由HTMLScriptRunner::executeParsingBlockingScripts()負責執行的。



一個典型的調用方式如下:

  

比如當新解析到一個Token時,Document Parser就會觸發一個canTakeNextToken來檢查一些狀態,其中一項就是看看指令碼是否可以運行,如果條件合適就會執行相應的指令碼。所謂條件合適,可以在它會調用的HTMLScriptRunner::isPendingScriptReady裡看到(函數也很好理解,就是看之前掛起的指令碼是不是又可以執行了) :

bool HTMLScriptRunner::isPendingScriptReady(const PendingScript& script){    m_hasScriptsWaitingForStylesheets = !m_document->haveStylesheetsLoaded();    if (m_hasScriptsWaitingForStylesheets)        return false;    if (script.cachedScript() && !script.cachedScript()->isLoaded())        return false;    return true;}


其中等待的就是兩個條件:

 1. 是不是正在載入CSS.

 2. 指令碼是不是有信賴的指令碼還在載入.


下面再介紹一個入口。因為有一部分指令碼是等待CSS載入完成後才會執行的,所以在CSS載入完成會一次調用過程如下:

  


2.4.2 async與defer指令碼的執行

下面就附兩張圖來說明兩者的執行過程,中間省略一些不重要的步驟。


 async的執行過程,  注意是由資源載入觸發:



 defer指令碼的執行過程,除了這個之外,還有一些異常的考慮,比如在HTMLScriptRunner析構時也會進行處理。

2.4.3 進一步閱讀代碼指引

以HTMLScriptRunner的runScript為例,它是由HTMLScriptRunner::execute()調用的,其實對應於W3C定義的標準流程: Running
a script:



這一部分的涉及到很多的細節,可以參考W3C的定義進一步閱讀代碼。

     http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html

  ScriptElement::prepareScript() -> http://dev.w3.org/html5/spec/Overview.html#prepare-a-script 


WebKit在重要函數裡也標註了對應標準文檔的章節,看代碼前可以先閱讀這部分的說明。



算是做個筆記吧,有時間再進一步研究。


轉載請註明出處:http://blog.csdn.net/horkychen

參考:WebKit研究

相關的UML圖可以到這裡下載 :WebKit Documentation on GitHub

系列索引:

基礎篇 (一)JSC與WebCore

基礎篇(二)解譯器基礎與JSC核心組件

基礎篇(三)從指令碼代碼到JIT編譯的代碼實現

基礎篇(四) 頁面解析與JavaScript元素的執行

進階篇(一) SSA (static single assignment)

進階篇(二) 類型推導(Type Inference)

進階篇(三) Register Allocation & Trampoline

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.