ホームページ >ウェブフロントエンド >jsチュートリアル >ブラウザが JavaScript スクリプトの読み込みとコード実行を実行する順序の分析_JavaScript スキル

ブラウザが JavaScript スクリプトの読み込みとコード実行を実行する順序の分析_JavaScript スキル

WBOY
WBOYオリジナル
2016-05-16 15:20:281665ブラウズ

この記事は主に、HTML ページに JavaScript を導入するいくつかの方法に基づいており、HTML での JavaScript スクリプトの実行順序を分析しています

1. JavaScript スクリプト実行のブロックの性質について

JavaScript には、ブラウザーで解析および実行されるときにブロック特性があります。つまり、JavaScript コードが実行されると、他のリソースの解析、レンダリング、ダウンロードが停止され、スクリプトが完了するまで待機する必要があります。これには議論の余地はなく、この動作はすべてのブラウザで一貫しています。その理由を理解するのは難しくありません。ブラウザには安定した DOM 構造が必要であり、JavaScript は DOM を変更する (DOM 構造を変更するか、特定の DOM ノードを変更する) 可能性があります。 JavaScript の実行中にページの解析が継続すると、解析プロセス全体の制御が困難になり、解析エラーが発生する可能性も非常に高くなります。

ただし、ここには注意が必要な別の問題があります。初期のブラウザでは、JavaScript ファイルのダウンロードによって、ページの解析がブロックされるだけでなく、他のリソースもブロックされます。ページ上のダウンロード (他の JavaScript スクリプト ファイル、外部 CSS ファイル、画像などの外部リソースを含む)。 IE8、Firefox3.5、Safari4、Chrome2 以降では、JavaScript の並行ダウンロードが許可されるようになり、同時に JavaScript ファイルのダウンロードは他のリソースのダウンロードをブロックしません (古いバージョンでは、JavaScript ファイルのダウンロードもブロックされます)。他のリソースのダウンロード)。

注: 同じドメイン名での最大接続数については、ブラウザごとに異なる制限があります。HTTP1.1 プロトコル仕様の要件では、2 を超えることはできませんが、現在、ほとんどのブラウザでは最大接続数が提供されています。 2 より多く、IE6/7 は両方とも 2、IE8 は 6 にアップグレードされ、Firefox と chrome も 6 になります。もちろん、この設定も変更できます。詳細については、http:// を参照してください。 www.stevesouders.com/blog/2008/03/20/roundup-on-Parallel-connections/

2. スクリプトの実行順序について

ブラウザはページを上から下の順に解析するため、通常の状況では、JavaScript スクリプトの実行順序も上から下になります。つまり、ページで最初に表示されるコード、または最初に導入されるコードがJavaScript ファイルの並列ダウンロードが許可されている場合でも、常に最初に実行されます。ここで「通常の状況下では」と赤でマークしていることに注意してください。その理由は何ですか? JavaScript コードを HTML に追加するには多くの方法があることがわかっており、それらは次のように要約されます (requirejs や seajs などのモジュール ローダーに関係なく):

(1) 通常の導入:
を介してページにスクリプト コードを導入するか、外部スクリプトを導入します。

(2) document.write メソッドを通じて 3f1c4e4b6b16bbbd69b2ee476dc4f83a コードをページに書き込みます

(3) 動的スクリプト技術を通じて、つまり、DOM インターフェースを使用して 3f1c4e4b6b16bbbd69b2ee476dc4f83a 要素を作成し、その要素の src を設定してから、その要素を DOM に追加します。

(4) Ajax を通じてスクリプトのコンテンツを取得し、3f1c4e4b6b16bbbd69b2ee476dc4f83a 要素を作成し、要素のテキストを設定して、要素を DOM に追加します。

(5) 要素のイベント ハンドラーに直接、または URL の本文として JavaScript コードを直接記述します。 例は次のとおりです。

<!--直接写在元素的事件处理程序中-->
<input type="button" value="点击测试一下" onclick="alert('点击了按钮')"/>
<!--作为URL的主体-->
<a href="javascript:alert('dd')">JS脚本作为URL的主体</a> 
5 番目のケースは、ここで説明しているスクリプトの実行順序に影響を与えないため、ここでは最初の 4 つのケースについてのみ説明します。


2.1 通常スクリプトを導入する場合

通常スクリプトを導入すると、JavaScriptコードは並列ダウンロードしても上から下へ導入順に実行されます。デモの例:


まず、PHP を通じてスクリプトを作成します。このスクリプトは、ファイル URL と遅延時間の 2 つのパラメーターを受け取り、受信した遅延時間の後にファイルの内容をブラウザーに送信します。


<&#63;php
$url = $_GET['url'];
$delay = $_GET['delay'];
if(isset($delay)){
sleep($delay);
}
echo file_get_contents($url);
&#63;> 
さらに、1.js と 2.js という 2 つの JavaScript ファイルも定義しました。この例では、2 つのコードは次のとおりです。


1.js

alert("私が最初のスクリプトです");

2.js


alert("私は 2 番目のスクリプトです");

次に、HTML のスクリプト コードを紹介します。


虽然第一个脚本延迟了3秒才会返回,但是在所有浏览器中,弹出的顺序也都是相同的,即:"我是第一个脚本"->"我是内部脚本"->"我是第二个脚本"

2.2 通过document.write向页面中写入脚本时

document.write在文档流没有关闭的情况下,会将内容写入脚本所在位置结束之后紧邻的位置,浏览器执行完当前短的代码,会接着解析document.write所写入的内容。

注:document.write写入内容的位置还存在一个问题,加入在93f0f5c25f18dab9d176bd4f6de5d30e内部的脚本中写入了93f0f5c25f18dab9d176bd4f6de5d30e标签内部不应该出现的内容,比如dc6dce4a544fdca2df29d5ac0ea9906b等内容标签等,则这段内容的起始位置将是6c04bd5ca3fcae76e30b72ad730ca86d标签的起始位置。

通过document.write写入脚本时存在一些问题,需要分类进行说明:

[1]同一个3f1c4e4b6b16bbbd69b2ee476dc4f83a标签中通过document.write只写入外部脚本:

在这种情况下,外部脚本的执行顺序总是低于引入脚本的标签内的代码,并且按照引入的顺序来执行,我们修改HTML中的代码:

<script src='/delayfile.php&#63;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&#63;url=http://localhost/js/load/2.js"><\/script>');
document.write('<script type="text/javascript" src="/delayfile.php&#63;url=http://localhost/js/load/1.js"><\/script>');
alert("我是内部脚本");
</script> 

这段代码执行完毕之后,DOM将被修改为:

而代码执行的结果也符合DOM中脚本的顺序:"我是第一个脚本"->"我是内部脚本"->"我是第二个脚本"->"我是第一个脚本"

[2]同一个3f1c4e4b6b16bbbd69b2ee476dc4f83a标签中通过document.write只写入内部脚本:

在这种情况下,通过documen.write写入的内部脚本,执行顺序的优先级与写入脚本标签内的代码相同,并且按照写入的先后顺序执行:

我们再修改HTML代码如下:

<script src='/delayfile.php&#63;url=http://localhost/js/load/1.js' type='text/javascript'></script>
<script type="text/javascript">
document.write('<script type="text/javascript">alert("我是docment.write写入的内部脚本")<\/script>');
alert("我是内部脚本");
document.write('<script type="text/javascript">alert("我是docment.write写入的内部脚本2222")<\/script>');
document.write('<script type="text/javascript">alert("我是docment.write写入的内部脚本3333")<\/script>');
</script> 

在这种情况下,document.write写入的脚本被认为与写入位置处的代码优先级相同,因此在所有浏览器中,弹出框的顺序均为:"我是第一个脚本"->"我是document.write写入的内部脚本"->"我是内部脚本"->"我是document.write写入的内部脚本2222"->"我是document.write写入的内部脚本3333"

[3]同一个3f1c4e4b6b16bbbd69b2ee476dc4f83a标签中通过document.write同时写入内部脚本和外部脚本时:

在这种情况下,不同的浏览器中存在一些区别:

在IE9及以下的浏览器中:只要是通过document.write写入的内部脚本,其优先级总是高于document.write写入的外部脚本,并且优先级与写入标签内的代码相同。而通过通过document.write写入的外部脚本,则总是在写入标签的代码执行完毕后,再按照写入的顺序执行;

而在其中浏览器中, 出现在第一个document.write写入的外部脚本之前的内部脚本,执行顺序的优先级与写入标签内的脚本优先级相同,而之后写入的脚本代码,不管是内部脚本还是外部脚本,总是要等到写入标签内的脚本执行完毕后,再按照写入的顺序执行。

我们修改以下HTML中的代码:

<script src='/delayfile.php&#63;url=http://localhost/js/load/1.js' type='text/javascript'></script><script type="text/javascript"> document.write('<script type="text/javascript">alert("我是docment.write写入的内部脚本")<\/script>'); alert("我是内部脚本"); document.write('<script type="text/javascript" src="/delayfile.php&#63;url=http://localhost/js/load/1.js"><\/script>'); document.write('<script type="text/javascript">alert("我是docment.write写入的内部脚本2222")<\/script>'); document.write('<script type="text/javascript" src="/delayfile.php&#63;url=http://localhost/js/load/1.js"><\/script>'); document.write('<script type="text/javascript">alert("我是docment.write写入的内部脚本3333")<\/script>'); alert("我是内部脚本2222");</script> 

在IE9及以下的浏览器中,上面代码执行后弹出的内容为:"我是第一个脚本"->"我是document.write写入的内部脚本"->"我是内部脚本"->"我是document.write写入的内部脚本2222"->"我是document.write写入的内部脚本3333"->"我是内部脚本2222"->"我是第一个脚本"->"我是第一个脚本"

其他浏览器中,代码执行后弹出的内容为:"我是第一个脚本"->"我是document.write写入的内部脚本"->"我是内部脚本"->"我是内部脚本2222"->"我是第一个脚本"->"我是document.write写入的内部脚本2222"->"我是第一个脚本"->"我是document.write写入的内部脚本3333"

如果希望IE及以下的浏览器与其他浏览器保持一致的行为,那么可选的做法就是把引入内部脚本的代码拿出来,单独放在后面一个新的3f1c4e4b6b16bbbd69b2ee476dc4f83a标签内即可,因为后面3f1c4e4b6b16bbbd69b2ee476dc4f83a标签中通过document.write所引入的代码执行顺序肯定是在之前的标签中的代码的后面的。

2.3 通过动态脚本技术添加代码时

通过动态脚本技术添加代码的主要目的在于创建无阻塞脚本,因为通过动态脚本技术添加的代码不会立刻执行,我们可以通过下面的load函数为页面添加动态脚本:

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);
}

但是通过动态脚本技术添加的外部JavaScript脚本不保证按照添加的顺序执行,这一点可以通过回调或者使用jQuery的html()方法,详细可参考:http://www.jb51.net/article/26446.htm

2.4 通过Ajax注入脚本

通过Ajax注入脚本同样也是添加无阻塞脚本的技术之一,我们首先需要创建一个XMLHttpRequest对象,并且实现get方法,然后通过get方法取得脚本内容并注入到文档中。

代码示例:

我们可以用如下代码封装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("无法创建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对象,再创建loadXhrScript函数:

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方法添加了一个参数,即是否异步,那么如果我们采用同步方法,通过Ajax注入的脚本肯定是按照添加的顺序执行;反之,如果我们采用异步的方案,那么添加的脚本的执行顺序肯定是无法确定的。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。