ホームページ >ウェブフロントエンド >jsチュートリアル >ブラウザが JavaScript スクリプトの読み込みとコード実行を実行する順序の分析_JavaScript スキル
この記事は主に、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>
2.1 通常スクリプトを導入する場合
<?php $url = $_GET['url']; $delay = $_GET['delay']; if(isset($delay)){ sleep($delay); } echo file_get_contents($url); ?>
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?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("我是内部脚本"); </script>
这段代码执行完毕之后,DOM将被修改为:
而代码执行的结果也符合DOM中脚本的顺序:"我是第一个脚本"->"我是内部脚本"->"我是第二个脚本"->"我是第一个脚本"
[2]同一个3f1c4e4b6b16bbbd69b2ee476dc4f83a标签中通过document.write只写入内部脚本:
在这种情况下,通过documen.write写入的内部脚本,执行顺序的优先级与写入脚本标签内的代码相同,并且按照写入的先后顺序执行:
我们再修改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写入的内部脚本")<\/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?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?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?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注入的脚本肯定是按照添加的顺序执行;反之,如果我们采用异步的方案,那么添加的脚本的执行顺序肯定是无法确定的。