>웹 프론트엔드 >JS 튜토리얼 >브라우저가 JavaScript 스크립트 로딩 및 코드 실행을 실행하는 순서 분석_javascript 기술

브라우저가 JavaScript 스크립트 로딩 및 코드 실행을 실행하는 순서 분석_javascript 기술

WBOY
WBOY원래의
2016-05-16 15:20:281705검색

이 글은 주로 HTML 페이지에 JavaScript를 도입하는 여러 가지 방법을 기반으로 HTML에서 JavaScript 스크립트의 실행 순서를 분석합니다

1. JavaScript 스크립트 실행의 차단 특성에 대하여

JavaScript는 브라우저에서 구문 분석되고 실행될 때 차단 특성을 가지고 있습니다. 즉, JavaScript 코드가 실행되면 다른 리소스의 구문 분석, 렌더링 및 다운로드를 중지하고 스크립트가 완료될 때까지 기다려야 합니다1. 이는 논란의 여지가 없으며 동작은 모든 브라우저에서 일관됩니다. 그 이유를 이해하는 것은 어렵지 않습니다. 브라우저에는 안정적인 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) 일반적인 소개: 즉, 페이지의 3f1c4e4b6b16bbbd69b2ee476dc4f83a 태그를 통해 스크립트 코드를 소개하거나 외부 스크립트를 소개합니다.

(2) document.write 메소드를 통해 페이지에 3f1c4e4b6b16bbbd69b2ee476dc4f83a 태그 또는 코드를 작성합니다.

(3) 동적 스크립트 기술을 통해, 즉 DOM 인터페이스를 사용하여 3f1c4e4b6b16bbbd69b2ee476dc4f83a 요소의 src를 설정한 다음 해당 요소를 DOM에 추가합니다.

(4) Ajax를 통해 스크립트 콘텐츠를 얻은 후 3f1c4e4b6b16bbbd69b2ee476dc4f83a 요소를 생성하고 요소의 텍스트를 설정한 다음 해당 요소를 DOM에 추가합니다.

(5) 요소의 이벤트 핸들러에 직접 JavaScript 코드를 작성하거나 URL의 본문으로 직접 작성합니다.

<!--直接写在元素的事件处理程序中-->
<input type="button" value="点击测试一下" onclick="alert('点击了按钮')"/>
<!--作为URL的主体-->
<a href="javascript:alert('dd')">JS脚本作为URL的主体</a> 

다섯 번째 사례는 논의 중인 스크립트 실행 순서에 영향을 미치지 않으므로 여기서는 처음 네 가지 사례만 논의합니다.

2.1 정상적으로 스크립트를 소개할 때

일반적으로 스크립트가 도입되면 JavaScript 코드는 스크립트가 병렬로 다운로드되는지 여부에 관계없이 도입된 순서대로 위에서 아래로 실행됩니다. 데모 예시:

먼저 PHP를 통해 스크립트를 작성합니다. 이 스크립트는 파일 URL과 지연 시간이라는 두 가지 매개변수를 받습니다. 스크립트는 들어오는 지연 시간 후에 파일 내용을 브라우저로 보냅니다.

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

또한 1.js와 2.js라는 두 개의 JavaScript 파일도 정의했습니다. 이 예에서 두 파일의 코드는 다음과 같습니다.

1.js

alert("내가 첫 번째 스크립트입니다");

2.js

alert("나는 두 번째 스크립트입니다");

그런 다음 HTML의 스크립트 코드를 소개합니다.

<script src='/delayfile.php&#63;url=http://localhost/js/load/1.js&delay=3' type='text/javascript'></script>
<script type="text/javascript">
alert("我是内部脚本");
</script>
<script src='/delayfile.php&#63;url=http://localhost/js/load/2.js&delay=1' type='text/javascript'></script> 

虽然第一个脚本延迟了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으로 문의하세요.