天天看點

浏覽器中javascript的執行過程 浏覽器中javascript的執行過程

轉自:http://www.cnblogs.com/lengyuhong/archive/2012/05/20/2509120.html

浏覽器中javascript的執行過程

    在講這個問題之前,先來補充幾個知識點,如果對此已經比較了解可以直接跳過

    1. 大多數浏覽器的元件構成如圖

浏覽器中javascript的執行過程 浏覽器中javascript的執行過程

    在最底層的三個元件分别是網絡,UI後端和js解釋器。作用如下:

    (1)網絡- 用來完成網絡調用,例如http請求,它具有平台無關的接口,可以在不同平台上工作

    (2)UI 後端- 用來繪制類似組合選擇框及對話框等基本元件,具有不特定于某個平台的通用接口,底層使用作業系統的使用者接口

    (3)JS解釋器- 用來解釋執行JS代碼

 ps:上圖和知識點主要來自《HOW BROWSERS WORK: BEHIND THE SCENES OF MODERN WEB BROWSERS》 想深入了解的同學可以重點看下。

      2. 大多數浏覽器(比如chrome)讓一個單線程共用于執行javascrip和更新使用者界面。這個線程通常被稱為“浏覽器UI線程”, 每個時刻隻能執行其中一種操作,這意味着當Javascript代碼正在執行時使用者界面無法響應輸入,反之亦然。這樣做是因為javascript代碼的作用就是操作DOM更新使用者界面,用同一個線程來做負責這兩件事情可以更高效

      3. 浏覽器UI線程的工作基于一個簡單的隊列系統,任務會被儲存到隊列中直到程序空閑。一旦空閑,隊列中的下一個任務就被重新提取出來并運作。這些任務要麼是運作javascript代碼,要麼執行UI更新,包括重繪和重排。

      4. 重點再強調下,javascript是單線程運作,千萬别被setTimeout()和setInterVal()這種函數迷惑而誤以為它是多線程。

     ok,基礎點講解完畢,讓我們進入正題,來講解在浏覽器中javascript的執行過程。

     一、原理

     一般而言,<script>标簽每次出現都會霸道地讓頁面等待腳本的解析和執行,無論目前的Javascript是内嵌的還是包含了外鍊檔案,頁面的下載下傳和渲染都必須停下來等待腳本執行完成。這在頁面的生存周期中是必要的,因為腳本執行過程中可能修改頁面内容,一個典型的例子就是在頁面中使用document.write()。

     當javascript代碼是内嵌在html裡面時,這點還是比較容易了解,但當javascript是外鍊檔案時稍微有點負載,因為存在一個加載過程,而且浏覽器加載好這個js檔案之後往往還對其緩存。

     首先,我們用以下這個例子來說明下緩存問題

浏覽器中javascript的執行過程 浏覽器中javascript的執行過程
<html>
<head> 
  <script type='text/javascript' src='js/f2.js'></script>
</head>
<body>
</body>
</html>       
浏覽器中javascript的執行過程 浏覽器中javascript的執行過程

     第一次打開頁面時:

浏覽器中javascript的執行過程 浏覽器中javascript的執行過程

    第二次打開頁面時:

浏覽器中javascript的執行過程 浏覽器中javascript的執行過程

    從上例中可以明顯看出,像chrome之類的高版本浏覽器會對js檔案進行緩存,作用是不言而喻,減少網絡請求。

  其次,第二個問題,當一個javascript檔案被加載時是否會阻塞其他javascript檔案或者其他檔案的加載。《高性能Javascript》一書中對這個問題做了較好的解答:各種浏覽器的低版本的處理是當一個javascript檔案在加載時,會同時阻塞頁面其他檔案的加載(包括其他javascript檔案),但IE8,Firfox3.5,Safari 4和Chrome 2都允許并行下載下傳javascript檔案,但遺憾的是,javascript下載下傳過程仍然會組舍其他資源的下載下傳,比如圖檔。盡管javascript腳本的下載下傳過程不會互相影響,但頁面仍然必須等待所有的javascript代碼下載下傳并執行完成才能繼續。

     這裡說句題外話:浏覽器對同一域名下的并發連結數也是有限制的,其他一些參數如下:

浏覽器中javascript的執行過程 浏覽器中javascript的執行過程

     二、技巧

     1. 腳本位置

         由于腳本會阻塞頁面其他資源的下載下傳,是以推薦将所有的<script>标簽放到<body>标簽的底部,已盡量減少對整個頁面下載下傳的影響。

     2. 将能合并的js檔案合并

     3. 無阻塞腳本

     現在比較常用的方法就是動态加載執行腳本。你的原理是通過DOM,你幾乎可以用Javascript動态建立HTML中的所有内容,其根本在于,<script>标簽與頁面中其他元素并無差異:都能通過DOM引用,都能在文檔中移動,删除和建立。檔案在改該<script>元素被添加到頁面時開始現在,它不會阻止其他檔案下載下傳,隻在執行階段阻塞渲染。特别強調:《高性能javascript》一文中說“這種技術的重點在于:無論何時啟動下載下傳,檔案的下載下傳和執行都不會阻塞頁面其他程序”,這并不是說它在執行不會阻塞其他javascript代碼,而是要強調不會阻塞其他資源的下載下傳等其他任務。

     具體的代碼如下:

浏覽器中javascript的執行過程 浏覽器中javascript的執行過程
function loadScript(url){
  var ga = document.createElement('script');
  ga.type = 'text/javascript'; 
  ga.async = true;
  ga.src = url;
  (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga); 
}      
浏覽器中javascript的執行過程 浏覽器中javascript的執行過程

     4. 神奇的setTimeout()

       這裡我不過多的将setTimeout()的原理,有興趣的讀者可以具體去看《高性能javascript》的第六章。我重點強調下,setTimeout的第二個參數并不是一個精确的時間,二是必須在javascript線程空閑時才能運作。利用這個特性,如下代碼簡單可以實作等待其他js代碼執行完畢後再執行function裡面的代碼。

setTimeout(function(){
  // do some before other javascripe codes had processed
}, 25)      

      但在function裡面不要使用document.write()方法,因為執行setTimeout裡面函數時往往已經到了頁面onload之後,此時再執行 document.write 将導緻目前頁面的内容被清空,因為它會自動觸發 document.open 方法。

參考文章:

《高性能Javascript》

HOW BROWSERS WORK: BEHIND THE SCENES OF MODERN WEB BROWSERS

Google Chrome源碼剖析【一】:多線程模型

javascript異步加載詳解