最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

探析瀏覽器執(zhí)行JavaScript腳本加載與代碼執(zhí)行順序

 更新時間:2016年01月12日 14:04:54   作者:TracyLin  
本文主要基于向HTML頁面引入JavaScript的幾種方式,分析HTML中JavaScript腳本的執(zhí)行順序問題,通過本文給大家分享瀏覽器執(zhí)行JavaScript腳本加載與代碼執(zhí)行順序,對瀏覽器執(zhí)行javascript及執(zhí)行順序相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧

本文主要基于向HTML頁面引入JavaScript的幾種方式,分析HTML中JavaScript腳本的執(zhí)行順序問題

1. 關(guān)于JavaScript腳本執(zhí)行的阻塞性

JavaScript在瀏覽器中被解析和執(zhí)行時具有阻塞的特性,也就是說,當JavaScript代碼執(zhí)行時,頁面的解析、渲染以及其他資源的下載都要停下來等待腳本執(zhí)行完畢①。這一點是沒有爭議的,并且在所有瀏覽器中的行為都是一致的,原因也不難理解:瀏覽器需要一個穩(wěn)定的DOM結(jié)構(gòu),而JavaScript可能會修改DOM(改變DOM結(jié)構(gòu)或修改某個DOM節(jié)點),如果在JavaScript執(zhí)行的同時還繼續(xù)進行頁面的解析,那么整個解析過程將變得難以控制,解析出錯的可能也變得很大。

然而這里還有一個問題需要注意,對于外部腳本,還涉及到一個腳本下載的過程,在早期的瀏覽器中,JavaScript文件的下載不僅會阻塞頁面的解析,甚至還會阻塞頁面其他資源的下載(包括其他JavaScript腳本文件、外部CSS文件以及圖片等外部資源)。從IE8、firefox3.5、safari4和chrome2開始允許JavaScript并行下載,同時JavaScript文件的下載也不會阻塞其他資源的下載(舊版本中,JavaScript文件的下載也會阻塞其他資源的下載)。

注:不同瀏覽器對于同一個域名下的最大連接數(shù)有不同的限制,HTTP1.1協(xié)議規(guī)范中的要求是不能高于2個,但是大多數(shù)瀏覽器目前實際提供的最大連接數(shù)都多于2個,IE6/7都是2個,IE8提升到了6個,firefox和chrome也是6個,當然這個設(shè)置也是可以修改的,詳細內(nèi)容可以參考:http://www.stevesouders.com/blog/2008/03/20/roundup-on-parallel-connections/

2. 關(guān)于腳本的執(zhí)行順序

瀏覽器是按照從上到下的順序解析頁面,因此正常情況下,JavaScript腳本的執(zhí)行順序也是從上到下的,即頁面上先出現(xiàn)的代碼或先被引入的代碼總是被先執(zhí)行,即使是允許并行下載JavaScript文件時也是如此。注意我們這里標紅了"正常情況下",原因是什么呢?我們知道,在HTML中加入JavaScript代碼有多種方式,概括如下(不考慮requirejs或seajs等模塊加載器):

(1)正常引入:即在頁面中通過<script>標簽引入腳本代碼或者引入外部腳本

(2)通過document.write方法向頁面寫入<script>標簽或代碼

(3)通過動態(tài)腳本技術(shù),即利用DOM接口創(chuàng)建<script>元素,并設(shè)置元素的src,然后再將元素添加進DOM中。

(4)通過Ajax獲取腳本內(nèi)容,然后再創(chuàng)建<script>元素,并設(shè)置元素的text,再將元素添加進DOM中。

(5)直接把JavaScript代碼寫在元素的事件處理程序中或直接作為URL的主體,示例如下:

<!--直接寫在元素的事件處理程序中-->
<input type="button" value="點擊測試一下" onclick="alert('點擊了按鈕')"/>
<!--作為URL的主體-->
<a href="javascript:alert('dd')">JS腳本作為URL的主體</a> 

第5種情況對于我們討論的腳本執(zhí)行順序沒有什么影響,因此我們這里只討論前四種情況:

2.1 正常引入腳本時

正常引入腳本時,JavaScript代碼會按照從上到下的順序執(zhí)行,不管腳本是不是并行下載,執(zhí)行時還是按照引入的順序從上到下執(zhí)行的,我們以下面的DEMO為例:

首先,通過PHP寫了一個腳本,這個腳本接收兩個參數(shù),文件URL和延遲時間,腳本會在傳入的延遲時間之后,將文件內(nèi)容發(fā)送給瀏覽器,腳本如下:

<?php
$url = $_GET['url'];
$delay = $_GET['delay'];
if(isset($delay)){
sleep($delay);
}
echo file_get_contents($url);
?> 

另外我們還定義了兩個JavaScript文件,分別為1.js和2.js,在這個例子中,二者的代碼分別如下:

1.js

alert("我是第一個腳本");

2.js

alert("我是第二個腳本");

然后,我們在HTML中引入腳本代碼:

<script src='/delayfile.php?url=http://localhost/js/load/1.js&delay=3' type='text/javascript'></script>
<script type="text/javascript">
alert("我是內(nèi)部腳本");
</script>
<script src='/delayfile.php?url=http://localhost/js/load/2.js&delay=1' type='text/javascript'></script> 

雖然第一個腳本延遲了3秒才會返回,但是在所有瀏覽器中,彈出的順序也都是相同的,即:"我是第一個腳本"->"我是內(nèi)部腳本"->"我是第二個腳本"

2.2 通過document.write向頁面中寫入腳本時

document.write在文檔流沒有關(guān)閉的情況下,會將內(nèi)容寫入腳本所在位置結(jié)束之后緊鄰的位置,瀏覽器執(zhí)行完當前短的代碼,會接著解析document.write所寫入的內(nèi)容。

注:document.write寫入內(nèi)容的位置還存在一個問題,加入在<head>內(nèi)部的腳本中寫入了<head>標簽內(nèi)部不應(yīng)該出現(xiàn)的內(nèi)容,比如<div>等內(nèi)容標簽等,則這段內(nèi)容的起始位置將是<body>標簽的起始位置。

通過document.write寫入腳本時存在一些問題,需要分類進行說明:

[1]同一個<script>標簽中通過document.write只寫入外部腳本:

在這種情況下,外部腳本的執(zhí)行順序總是低于引入腳本的標簽內(nèi)的代碼,并且按照引入的順序來執(zhí)行,我們修改HTML中的代碼:

<script src='/delayfile.php?url=http://localhost/js/load/1.js&delay=2' type='text/javascript'></script>
<script type="text/javascript">
document.write('<script type="text/javascript" src="/delayfile.php?url=http://localhost/js/load/2.js"><\/script>');
document.write('<script type="text/javascript" src="/delayfile.php?url=http://localhost/js/load/1.js"><\/script>');
alert("我是內(nèi)部腳本");
</script> 

這段代碼執(zhí)行完畢之后,DOM將被修改為:

而代碼執(zhí)行的結(jié)果也符合DOM中腳本的順序:"我是第一個腳本"->"我是內(nèi)部腳本"->"我是第二個腳本"->"我是第一個腳本"

[2]同一個<script>標簽中通過document.write只寫入內(nèi)部腳本:

在這種情況下,通過documen.write寫入的內(nèi)部腳本,執(zhí)行順序的優(yōu)先級與寫入腳本標簽內(nèi)的代碼相同,并且按照寫入的先后順序執(zhí)行:

我們再修改HTML代碼如下:

<script src='/delayfile.php?url=http://localhost/js/load/1.js' type='text/javascript'></script>
<script type="text/javascript">
document.write('<script type="text/javascript">alert("我是docment.write寫入的內(nèi)部腳本")<\/script>');
alert("我是內(nèi)部腳本");
document.write('<script type="text/javascript">alert("我是docment.write寫入的內(nèi)部腳本2222")<\/script>');
document.write('<script type="text/javascript">alert("我是docment.write寫入的內(nèi)部腳本3333")<\/script>');
</script> 

在這種情況下,document.write寫入的腳本被認為與寫入位置處的代碼優(yōu)先級相同,因此在所有瀏覽器中,彈出框的順序均為:"我是第一個腳本"->"我是document.write寫入的內(nèi)部腳本"->"我是內(nèi)部腳本"->"我是document.write寫入的內(nèi)部腳本2222"->"我是document.write寫入的內(nèi)部腳本3333"

[3]同一個<script>標簽中通過document.write同時寫入內(nèi)部腳本和外部腳本時:

在這種情況下,不同的瀏覽器中存在一些區(qū)別:

在IE9及以下的瀏覽器中:只要是通過document.write寫入的內(nèi)部腳本,其優(yōu)先級總是高于document.write寫入的外部腳本,并且優(yōu)先級與寫入標簽內(nèi)的代碼相同。而通過通過document.write寫入的外部腳本,則總是在寫入標簽的代碼執(zhí)行完畢后,再按照寫入的順序執(zhí)行;

而在其中瀏覽器中, 出現(xiàn)在第一個document.write寫入的外部腳本之前的內(nèi)部腳本,執(zhí)行順序的優(yōu)先級與寫入標簽內(nèi)的腳本優(yōu)先級相同,而之后寫入的腳本代碼,不管是內(nèi)部腳本還是外部腳本,總是要等到寫入標簽內(nèi)的腳本執(zhí)行完畢后,再按照寫入的順序執(zhí)行。

我們修改以下HTML中的代碼:

<script src='/delayfile.php?url=http://localhost/js/load/1.js' type='text/javascript'></script><script type="text/javascript"> document.write('<script type="text/javascript">alert("我是docment.write寫入的內(nèi)部腳本")<\/script>'); alert("我是內(nèi)部腳本"); document.write('<script type="text/javascript" src="/delayfile.php?url=http://localhost/js/load/1.js"><\/script>'); document.write('<script type="text/javascript">alert("我是docment.write寫入的內(nèi)部腳本2222")<\/script>'); document.write('<script type="text/javascript" src="/delayfile.php?url=http://localhost/js/load/1.js"><\/script>'); document.write('<script type="text/javascript">alert("我是docment.write寫入的內(nèi)部腳本3333")<\/script>'); alert("我是內(nèi)部腳本2222");</script> 

在IE9及以下的瀏覽器中,上面代碼執(zhí)行后彈出的內(nèi)容為:"我是第一個腳本"->"我是document.write寫入的內(nèi)部腳本"->"我是內(nèi)部腳本"->"我是document.write寫入的內(nèi)部腳本2222"->"我是document.write寫入的內(nèi)部腳本3333"->"我是內(nèi)部腳本2222"->"我是第一個腳本"->"我是第一個腳本"

其他瀏覽器中,代碼執(zhí)行后彈出的內(nèi)容為:"我是第一個腳本"->"我是document.write寫入的內(nèi)部腳本"->"我是內(nèi)部腳本"->"我是內(nèi)部腳本2222"->"我是第一個腳本"->"我是document.write寫入的內(nèi)部腳本2222"->"我是第一個腳本"->"我是document.write寫入的內(nèi)部腳本3333"

如果希望IE及以下的瀏覽器與其他瀏覽器保持一致的行為,那么可選的做法就是把引入內(nèi)部腳本的代碼拿出來,單獨放在后面一個新的<script>標簽內(nèi)即可,因為后面<script>標簽中通過document.write所引入的代碼執(zhí)行順序肯定是在之前的標簽中的代碼的后面的。

2.3 通過動態(tài)腳本技術(shù)添加代碼時

通過動態(tài)腳本技術(shù)添加代碼的主要目的在于創(chuàng)建無阻塞腳本,因為通過動態(tài)腳本技術(shù)添加的代碼不會立刻執(zhí)行,我們可以通過下面的load函數(shù)為頁面添加動態(tài)腳本:

function loadScript(url,callback){
var script = document.createElement("script");
script.type = "text/javascript";
//綁定加載完畢的事件
if(script.readyState){
script.onreadystatechange = function(){
if(script.readyState === "loaded" || script.readyState === "complete"){
callback&&callback();
}
}
}else{
script.onload = function(){
callback&&callback();
}
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}

但是通過動態(tài)腳本技術(shù)添加的外部JavaScript腳本不保證按照添加的順序執(zhí)行,這一點可以通過回調(diào)或者使用jQuery的html()方法,詳細可參考:http://m.fzitv.net/article/26446.htm

2.4 通過Ajax注入腳本

通過Ajax注入腳本同樣也是添加無阻塞腳本的技術(shù)之一,我們首先需要創(chuàng)建一個XMLHttpRequest對象,并且實現(xiàn)get方法,然后通過get方法取得腳本內(nèi)容并注入到文檔中。

代碼示例:

我們可以用如下代碼封裝XMLHttpRequest對象,并封裝其get方法:

var xhr = (function(){
function createXhr(){
var xhr ;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else if(window.ActiveXObject){
var xhrVersions = ['MSXML2.XMLHttp','MSXML2.XMLHttp.3.0','MSXML2.XMLHttp.6.0'], i, len;
for(i = 0, len = xhrVersions.length; i < len ; i++){
try{
xhr = new ActiveXObject(xhrVersions[i]);
}catch(e){
}
}
}else{
throw new Error("無法創(chuàng)建xhr對象");
}
return xhr;
}
function get(url,async,callback){
var xhr = createXhr();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
callback&&callback(xhr.responseText);
}else{
alert("請求失敗,錯誤碼為" + xhr.status);
}
}
}
xhr.open("get",url,async);
xhr.send(null);
}
return {
get:get
}
}()) 

然后基于xhr對象,再創(chuàng)建loadXhrScript函數(shù):

function loadXhrScript(url,async, callback){ if(async == undefined){ async = true; } xhr.get(url,async,function(text){ var script = document.createElement("script"); script.type = "text/javascript"; script.text = text; document.body.appendChild(script); });} 

我們上面的get方法添加了一個參數(shù),即是否異步,那么如果我們采用同步方法,通過Ajax注入的腳本肯定是按照添加的順序執(zhí)行;反之,如果我們采用異步的方案,那么添加的腳本的執(zhí)行順序肯定是無法確定的。

相關(guān)文章

  • JavaScript?中的異步?forEach

    JavaScript?中的異步?forEach

    這篇文章主要介紹了JavaScript異步forEach的相關(guān)知識,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • 判斷瀏覽器的javascript版本的代碼

    判斷瀏覽器的javascript版本的代碼

    話說最近在研究某著名跟蹤系統(tǒng),在其給用戶的實施代碼中有一段判斷瀏覽器Javascript版本的代碼引起了我的注意,于是問了下google如何判斷瀏覽器的javascript版本
    2010-09-09
  • 使用weixin-java-tools完成微信授權(quán)登錄、微信支付的示例

    使用weixin-java-tools完成微信授權(quán)登錄、微信支付的示例

    這篇文章主要介紹了使用weixin-java-tools完成微信授權(quán)登錄、微信支付的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • js中數(shù)組解構(gòu)與對象解構(gòu)示例代碼

    js中數(shù)組解構(gòu)與對象解構(gòu)示例代碼

    數(shù)組解構(gòu)是一種在 JavaScript 中從數(shù)組中提取值并將它們分配給變量的方式,在數(shù)組解構(gòu)中分為完全解構(gòu),不完全解構(gòu),解構(gòu)失敗以及解構(gòu)默認值,這篇文章主要介紹了js中數(shù)組解構(gòu)與對象解構(gòu),需要的朋友可以參考下
    2023-09-09
  • 使用微信SDK自定義分享的方法

    使用微信SDK自定義分享的方法

    這篇文章主要介紹了使用微信SDK自定義分享的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • JS實現(xiàn)將人民幣金額轉(zhuǎn)換為大寫的示例代碼

    JS實現(xiàn)將人民幣金額轉(zhuǎn)換為大寫的示例代碼

    本篇文章主要是對使用JS實現(xiàn)將人民幣金額轉(zhuǎn)換為大寫的示例代碼進行了介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2014-02-02
  • JavaScript創(chuàng)建對象的幾種方式小結(jié)

    JavaScript創(chuàng)建對象的幾種方式小結(jié)

    這篇文章主要介紹了 JavaScript 中創(chuàng)建對象的多種方式,包括字面量方式、構(gòu)造函數(shù)方式、原型模式、() 方法、ES6 類,并分別說明了它們的特點和適用場景,強調(diào)在實際編程中要根據(jù)需求選擇合適的方式以提高代碼質(zhì)量和性能,需要的朋友可以參考下
    2024-12-12
  • javascript監(jiān)聽頁面刷新和頁面關(guān)閉事件方法詳解

    javascript監(jiān)聽頁面刷新和頁面關(guān)閉事件方法詳解

    本文主要介紹了javascript監(jiān)聽頁面刷新和頁面關(guān)閉事件的方法,具有一定的參考價值,下面跟著小編一起來看下吧
    2017-01-01
  • canvas軌跡回放功能實現(xiàn)

    canvas軌跡回放功能實現(xiàn)

    這篇文章主要介紹了canvas軌跡回放功能實現(xiàn)過程以及相關(guān)的代碼整理,跟著小編一起學(xué)習(xí)下吧。
    2017-12-12
  • JavaScript使用RegExp進行正則匹配的方法

    JavaScript使用RegExp進行正則匹配的方法

    這篇文章主要介紹了JavaScript使用RegExp進行正則匹配的方法,實例分析了RegExp對象在進行正則匹配時的相關(guān)使用技巧,需要的朋友可以參考下
    2015-07-07

最新評論

克山县| 珲春市| 咸丰县| 山西省| 定南县| 邵武市| 法库县| 淮阳县| 长葛市| 康乐县| 定安县| 渑池县| 融水| 潞城市| 南川市| 炉霍县| 松江区| 万宁市| 新宾| 淅川县| 怀集县| 峨边| 承德市| 贵溪市| 建平县| 讷河市| 平定县| 正定县| 遵化市| 疏附县| 广河县| 河东区| 秀山| 高陵县| 察雅县| 安顺市| 营山县| 青田县| 深泽县| 阳信县| 满城县|