承接前面一篇文章《瀏覽器的渲染原理簡介》 ,本文來說下JavaScript的載入與執行。
通常來說,瀏覽器對於JavaScript 的運作有兩大功能:
#1) 載入後馬上執行
2 ) 執行時會阻塞頁面後續的內容(包括頁面的渲染、其他資源的下載)
所以,如果有多個JS文件被引入,那麼對於瀏覽器來說,這些JS文件將會被串列地載入並依序執行。
由於JavaScript 可能會操作 HTML文件的DOM 樹,所以瀏覽器一般都不會像並行下載CSS文件一樣並行下載JS文件,這是JS文件的特殊性造成的。因此,如果你的JavaScript想操作後面的DOM 元素,瀏覽器會報錯說找不到對象,這是因為JavaScript執行時後面的HTML被阻塞住了,操作DOM 樹時還沒有後面的節點。
傳統方式
#當你寫下以下程式碼時:
<script type="text/javascript" src="http://coolshell.cn/asyncjs/alert.js"></script>
基本上來說,head裡的3f1c4e4b6b16bbbd69b2ee476dc4f83a標籤會阻塞後續資源的載入以及整個頁面的產生。例如上面這個範例,其中只有一句JS程式碼(範例):
alert(“hello world”) ;
效果是在載入此JS檔案時會彈出一個對話框,因此點選這個對話框後才會對後續資源進行載入以及對整個頁面的進行產生。
所以,有很多網站會把 js 放在網頁的最後面,或是用 window.load、$(document).ready(function(){}) 之類的事件。
另外,由於絕大多數的JavaScript程式碼並不需要等待頁面,我們需要非同步載入功能。那我們怎麼異步載入呢?
document.write 方式
#你可能以為 document.write() 方式能夠解決不阻塞的方式。透過 document.write 方法寫入3f1c4e4b6b16bbbd69b2ee476dc4f83a標籤的方式就可以執行後面的東西,對於在同一個 script 標籤內的 JS程式碼來說是這樣的。但是,對於整個頁面來說,還是會阻塞的。以下是一段測試程式碼(範例):
<script type="text/javascript" language="javascript"> function loadjs(script_filename) { document.write('<' + 'script language="javascript" type="text/javascript"'); document.write(' src="' + script_filename + '">'); document.write('<'+'/script'+'>'); alert("loadjs() exit..."); } var script = 'http://coolshell.cn/asyncjs/alert.js'; loadjs(script); alert("loadjs() finished!");</script> <script type="text/javascript" language="javascript"> alert("another block");</script>
依此彈出的對話方塊為:
loadjs() exit... loadjs() finished! hello world another block
然後才會顯示頁面。
script 的defer和async屬性
#IE自從IE6就之處defer 標籤,如:
<script defer type="text/javascript" src="./alert.js" ></script>
對於IE來說,這個標籤會讓IE並行下載JS文件,並且把其執行hold到了整個DOM裝載完畢,多個defer 的3f1c4e4b6b16bbbd69b2ee476dc4f83a在執行時也會按照其出現的順序來運行。最重要的是3f1c4e4b6b16bbbd69b2ee476dc4f83a被加上refer 後,其不會阻塞後續DOM 的渲染。但因為refer 只是IE專用,所以一般用的比較少。
我們的HMTL 5也加入了一個非同步載入 JavaScript的屬性:async 。無論你對它賦有什麼樣的值,只要它出現,它就開始非同步載入 JS檔。但是,async的非同步載入會有一個比較嚴重的問題,那就是它忠實的執行「載入後馬上執行」這條規則。所以,雖然它不會阻塞頁面的渲染,但你也無法控制他執行的順序和時機(範例)。
支援 async 標籤的瀏覽器如下,Opera還不支援(來自這裡),所以這個方法也不是太好。
# 動態建立DOM的方式
##這種方式可能是用的最多的了。function loadjs(script_filename) { var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.setAttribute('src', script_filename); script.setAttribute('id', 'coolshell_script_id'); script_id = document.getElementById('coolshell_script_id'); if(script_id){ document.getElementsByTagName('head')[0].removeChild(script_id); } document.getElementsByTagName('head')[0].appendChild(script); } var script = 'http://coolshell.cn/asyncjs/alert.js'; loadjs(script);這種方式幾乎成了標準的非同步載入js檔案的方式(範例)。這種方式也玩出了 jsonp 的東東。也就是我們可以為script的src 指定某個後台的腳本(例如PHP),而這個PHP回傳一個JavaScript函數,其參數是一個json 字串,回傳來呼叫我們預先定義好的 JavaScript 函數。作者的參考範例:t.js (這個範例是作者之前在微博徵集的一個非同步ajax呼叫的小例子)
按需異步載入JS
上面的DOM方式的例子解決了非同步載入JavaScript的問題,但沒有解決我們想讓他按我指定的時機運行的問題。所以,我們需要把上面的DOM方式綁定到某個事件就可以了。 例如:1) 綁在window.load 事件上(範例)
window.load = loadjs("http://coolshell.cn/asyncjs/alert.js")
2) 綁在特定的事件上(範例)
<p style="cursor: pointer" onclick="LoadJS()">Click to load alert.js </p>例如當我們在點擊某個DOM元素時,才載入我們的JS檔案。
更多#
有的人可能会觉得绑定在某个特定事件上似乎过了一点,而在点击时才载入JS又太慢了。这里抛出一个终极问题:我们想要异步地把JS文件下载到用户本地,但是又不执行,仅当我们想要执行的时候才去执行。
作者提出了一种方式,就像多年之前玩preload图片那样,我们可以动用 object 标签(也可以使用 iframe 标签),于是有了下面的代码(示例):
function cachejs(script_filename){ var cache = document.createElement('object'); cache.data = script_filename; cache.id = "coolshell_script_cache_id"; cache.width = 0; cache.height = 0; document.body.appendChild(cache); }
在Chrome 下按F12(或者Ctrl+Shit+I),切换到 network页,可以看到 alert.js 文件已经下载了但是却没有执行弹出 "hello,world"对话框的操作。然后我们再用之前“绑在特定的事件上”的方式,因为浏览器端有缓存了,不会在从服务器上下载 alert.js 文件了,这样就能保证执行速度了。
我们还可以用Ajax的方式,比如:
var xhr = new XMLHttpRequest(); xhr.open('GET', 'new.js'); xhr.send('');
最后再提两个JS库,一个是ControlJS,一个叫HeadJS,专门用来做异步load javascript文件的。
来源:JavaScript 的装载和执行
以上是詳解JavaScript中的裝載與執行的詳細內容。更多資訊請關注PHP中文網其他相關文章!