2008年12月18日 星期四

「javascript」引入js 檔執行順序, 這點要明瞭,日後才不會出現莫名的問題

引用:http://www.busfly.cn/csdn/post/JavaScript-JS-seq.html

近來我通過一些測試以全面的解析網頁在各種瀏覽器中的JavaScript代碼的執行順序,在這兒做個記錄。
我們知道javaScript是一種解釋型語言,他的執行是自上而下,但是各個瀏覽器對於至上而下的理解是有細微差別的,而代碼的上下游也就是程序流又對於程序正確至關重要,所以我覺得有必要深入理解多個js塊兒的執行順序。(轉帖請註明出處:http://blog.csdn.net/lenel)

首先得知道有多少方法能把javaScript加入到頁面中呢?常見下述的前2種,其實還有更多。(轉帖請註明出處:http://blog.csdn.net/lenel)
1.頁面中直接引入外部js文件:<script src="my.js"></script>

2.頁面中直接寫如js片段<script>alert(1)</script>

3.在js中引入js文件document.write("<scr"+"ipt src='my.js'></scr"+"ipt>");
注意:這時候"..</script>"必須拆成"</scr"+"ipt>",否則瀏覽器可能會把父js片段關閉掉,出錯;

4.同樣在js中引用其他js片段,document.write("<scr"+"ipt>alert(1)</scr"+"ipt>");
你可能覺得這個並沒有必要,既然已經在script中了還套一層幹嘛?呵呵,怎麼說也是一種寫法,而且它具有其特殊的行為,稍後我們討論到。

5.使用Ajax中的xmlHttpRequest結合eval()來引入js,我最早在Dojo的代碼見到,寫的詳細些:
var ajaxRequest = getXmlHttpRequest()//省去各個瀏覽器得到xmlHttpRequest的部門
ajaxRequest.open("GET","my.js",false);//使用xmlHttpRequest對象Get方法的同步調用
ajaxRequest.send(null);
sJsFragment = ajax.responseText;//得到字符串為js片段
eval(sJsFragment);//執行js片段
注意:這裡要求my.js即後來的sJsFragment內容得是非常規範的js,切沒有//開頭的註釋,怎樣檢查js是否規範呢?去http://jslint.com/

6.無所不能的Dom方法,這個我最早在Yahoo的前端代碼中見到,非常厲害,也寫的詳細些:
var oScript = document.createElement("script");//創建一個Script元素
oScript.src = "my.js";//制定src屬性
document.getElementsByTagName("head")[0].appendChild(oScript);
說明:my.js的內容會在oScript加入到文檔中之後獲得並執行。仔細看下這段容易發現這個調用是異步的,可以在文檔載入之後通過事件觸發,我用它變通了一下,作為了xmlHttpRequest的Get方法在跨域取數時的替代,獲得了很完美的效果,以後有機會專門寫篇文。

六種不少吧,可能還會有吧,而且這幾種之間還可能相互嵌套,變化無常。
其中1、2、4、6種方式引入的javaScript的執行順序是非常自然的,隨著頁面的載入以及後續的事件觸發,它們遵守先來後到、而其內部自上而下。

我們主要關注的是第3、4種引入js方法帶來的問題(轉帖請註明出處:http://blog.csdn.net/lenel)
測試準備:
編寫文件1.js內容只有一行:(轉帖請註明出處:http://blog.csdn.net/lenel)
alert("1.1");
編寫文件3.js內容只有一行:(轉帖請註明出處:http://blog.csdn.net/lenel)
alert("3.1");
再編寫test.html(轉帖請註明出處:http://blog.csdn.net/lenel)
<html>
<head>
<script>//按第2種方式引入的腳本
document.write("<scr"+"ipt>alert(4.1);</scr"+"ipt>");//按第4種方式引入的第一個腳本
alert(2.1);
document.write("<scr"+"ipt>alert(4.2);</scr"+"ipt>");//按第4種方式引入的第二個腳本
document.write("<scr"+"ipt src='3.js'></scr"+"ipt>");//按第3種方式引入的腳本
document.write("<scr"+"ipt>alert(4.3);</scr"+"ipt>");//按第4種方式引入的第三個腳本
alert(2.2);
</script>
<script src="1.js">//按第1種方式引入的腳本</script>
</head>
<body>
</body>
</html>

執行結果:
IE瀏覽器的alert順序為: 4.1 - 2.1 - 4.2 - 4.3 - 2.2 - 3.1 - 1.1(轉帖請註明出處:http://blog.csdn.net/lenel)
FireFox瀏覽器的alert順序為: 4.1 - 2.1 - 4.2 - 2.2 - 3.1 - 4.3 - 1.1(轉帖請註明出處:http://blog.csdn.net/lenel)
Opera瀏覽器的alert順序為: 4.1 - 2.1 - 4.2 - 3.1 - 4.3 - 2.2 - 1.1(轉帖請註明出處:http://blog.csdn.net/lenel)

分析:
IE:把第3種方式引入的js文件放在當前腳本單元之後接著執行,把第4種方式引入的js當作宿主腳本單元的一部分,在內部其引入的位置執行。
Opera:把第3種和第4種方式引入的js都當作宿主腳本單元的一部分,在內部其引入的位置執行。
FireFox:第3種方式引入的js文件放在當前腳本單元之後接著執行;在宿主引入第3種腳本之前,把第4種方式引入的js當作宿主腳本單元的一部分,在內部其引入的位置執行;在宿主引入第3種腳本之後,把第4種方式引入的js當作單獨的一個腳本塊兒,在宿主腳本執行之後接著執行。FirefFox的這種做法是很不友好。

結論:
瀏覽器差別巨大,所以不要指望通過第3、4種方式引入的腳本在引入位置或者宿主腳本執行後執行,如果你要至少支持FireFox瀏覽器,那麼通過這種邏輯控制程序流會有大麻煩,應該避免。

實例:
以發佈Alimama的廣告代碼為例(google adsense、yahoo定向推廣也一樣)。
多數站CMS系統管理廣告代碼,常常需要js的方式來做代碼調用。這樣如果想通過一個js發佈多塊兒廣告位就會出現麻煩,比如在一個js中:
第一種寫法:
/發佈第一塊兒廣告
var alimama_pid=xxxx_1;
document.write("<scr"+"ipt src='http://p.alimama.com/inf.js'></scr"+"ipt>");
//發佈第二塊兒廣告
var alimama_pid=xxxx_2;
document.write("<scr"+"ipt src='http://p.alimama.com/inf.js'></scr"+"ipt>");
希望引入的js塊和js文件按書寫順序執行,根據上述測試的結論,針對這個任務,IE和FireFox是得不到想要的結果的,Opera可以。

第二種寫法:
//發佈第一塊兒廣告
document.write("<scr"+"ipt>var alimama_pid=xxxx_1</scr"+"ipt>");
document.write("<scr"+"ipt src='http://p.alimama.com/inf.js'></scr"+"ipt>");
//發佈第二塊兒廣告
document.write("<scr"+"ipt>var alimama_pid=xxxx_2</scr"+"ipt>");
document.write("<scr"+"ipt src='http://p.alimama.com/inf.js'></scr"+"ipt>");
仍然根據上述測試的結論,IE是得不到想要的結果的,Opera乾得很漂亮,而且蹩腳的FireFox居然也可以。

再說一個關於setTimeout()控制程序流的問題(轉帖請註明出處:http://blog.csdn.net/lenel)
下面一段腳本在頁面加載時執行
function fn(){
alert("a");
}
alert(1);
window.setTimeout(fn,3000);
alert(2);

各種瀏覽器alert的順序都是1 - 2 - a。
也就是說setTimeout()方法不會讓程序流停止,等候指定時間後再執行其下的語句,而只是告訴瀏覽器要在指定時間後執行參數中的方法。所以在程序載入過程中,不要指望setTimeout來造成程序流等待

【下列文章您可能也有興趣】

沒有留言: