手机版

浏览器执行中JavaScript脚本加载和代码执行顺序分析

时间:2021-09-18 来源:互联网 编辑:宝哥软件园 浏览:

基于在HTML页面中引入JavaScript的几种方式,分析了JavaScript脚本在HTML中的执行顺序。

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个,火狐和chrome中的6个。当然,这个设置是可以修改的。详情请见:http://www . stevesouders.com/blog/2008/03/20/并联综述/

2.关于脚本的执行顺序。

浏览器从上到下解析页面,所以正常情况下,JavaScript脚本的执行顺序也是从上到下,也就是页面上最先出现或者最先引入的代码总是最先执行,即使允许并行下载JavaScript文件也是如此。请注意,我们在这里标记了“在正常情况下”。原因是什么?我们知道,在HTML中添加JavaScript代码的方法有很多,总结如下(不考虑requirejs或seajs等模块加载器):

(1)正常介绍:即通过页面中的script标签介绍脚本代码或外部脚本。

(2)通过document.write方法将脚本标记或代码写入页面。

(3)使用动态脚本技术,即使用DOM接口创建脚本元素,并设置元素的src,然后将元素添加到DOM中。

(4)通过Ajax获取脚本内容,然后创建脚本元素,设置元素的文本,然后将元素添加到DOM中。

(5)直接在元素的事件处理程序中或者直接作为URL的主体编写JavaScript代码。例子如下:

!-将其直接写入元素的事件处理程序-input type=' button ' value=' click to test ' onclick=' alert(' clicked the button ')'/!-作为URL的主体-a href=' JavaScript : alert(' DD ')' js脚本作为URL的主体/a第五种情况对我们讨论的脚本执行顺序没有影响,所以这里只讨论前四种情况:

2.1正常介绍脚本时。

正常介绍脚本时,无论脚本是否并行下载,JavaScript代码都会从上到下执行,脚本会按照介绍的顺序从上到下执行。让我们以下面的演示为例:

首先,通过PHP编写一个脚本。这个脚本接收两个参数,文件网址和延迟时间。脚本将在传入延迟时间后将文件内容发送到浏览器。脚本如下:

?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中:

脚本src='/delayfile.php?URL=http://localhost/js/load/1 . jsdelay=3 ' type=' text/JavaScript '/script script type=' text/JavaScript ' alert('我是内部脚本');/script script src='/delayfile . PHP?URL=http://localhost/js/load/2 . jsdelay=1 ' type=' text/JavaScript '/script虽然第一个脚本延迟3秒后会返回,但弹出顺序在所有浏览器中都是一样的,即‘我是第一个脚本’-‘我是内部脚本’-‘我是第二个脚本’。

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

在文档流未关闭的情况下,document.write会在脚本结束后立即将内容写入该位置。浏览器执行当前的短代码后,将解析document.write编写的内容。

注意:写入document.write的位置仍有问题。如果添加在head内部的脚本写了一些不应该出现在head标签内部的内容,比如div和其他内容标签,那么这段内容的起始位置将是body标签的起始位置。

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

[1]只有外部脚本通过document写入同一脚本标记。write:

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

脚本src='/delayfile.php?URL=http://localhost/js/load/1 . jsdelay=2 ' type=' text/JavaScript '/script script type=' text/JavaScript ' document . write(' script type=' text/JavaScript ' src=' http :/delayfile . PHP?URL=http://localhost/js/load/2 . js ' \/script ');document . write(' script type=' text/JavaScript ' src=' http :/delayfile . PHP?URL=http://localhost/js/load/1 . js ' \/script ');alert(‘我是内部脚本’);代码/脚本执行后,DOM将被修改为:

代码执行的结果也符合DOM中脚本的顺序:‘我是第一个脚本’—‘我是内部脚本’—‘我是第二个脚本’—‘我是第一个脚本’。

[2]只有内部脚本通过document写在同一个脚本标记中。write:

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

我们修改HTML代码如下:

脚本src='/delayfile.php?URL=http://localhost/js/load/1 . js ' type=' text/JavaScript '/script script type=' text/JavaScript ' docment . write(' script type=' text/JavaScript ' alert('我是documentwrite写的内部脚本')\/script ');alert(‘我是内部脚本’);document . write(' script type=' text/JavaScript ' alert('我是docment.write 2222 '编写的内部脚本' \/script ');Document.write(' script type=' text/JavaScript ' alert('我是document . write编写的内部脚本3333 ')\/script ');/script在这种情况下,document.write编写的脚本被认为与编写位置的代码具有相同的优先级,因此在所有浏览器中,弹出框的顺序为:‘我是第一个脚本’-‘我是document.write编写的内部脚本’-‘我是document.write编写的内部脚本’-‘我是document.write 222编写的内部脚本’-‘我是document.write编写的内部脚本。

[3]当内部脚本和外部脚本通过document写入同一脚本标记时。write:

在这种情况下,不同浏览器之间存在一些差异:

在IE9及以下的浏览器中,只要document.write写的内部脚本总是比document.write写的外部脚本有更高的优先级,优先级和标签中写的代码是一样的。通过document.write编写的外部脚本总是在写入标签的代码被执行之后按照编写的顺序执行。

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

我们在下面的HTML中修改代码:

脚本src='/delayfile.php?URL=http://localhost/js/load/1 . js ' type=' text/JavaScript '/script script type=' text/JavaScript ' docment . write(' script type=' text/JavaScript ' alert('我是documentwrite写的内部脚本')\/script ');alert(‘我是内部脚本’);document . write(' script type=' text/JavaScript ' src=' http :/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=' http :/delayfile . PHP?URL=http://localhost/js/load/1 . js ' \/script ');Document.write(' script type=' text/JavaScript ' alert('我是document . write编写的内部脚本3333 ')\/script ');alert(‘我是内部脚本2222’);/script在IE9及以下浏览器中,执行上述代码后弹出的内容为:‘我是第一个脚本’-‘我是document . write’编写的内部脚本——‘我是document . write 2222’编写的内部脚本——‘我是document . write 3333’编写的内部脚本——‘我是内部脚本2222’-。

在其他浏览器中,代码执行后弹出的内容有:‘我是第一个脚本’—‘我是文档写的内部脚本,写’—‘我是内部脚本2222’—‘我是第一个脚本’—‘我是文档写的内部脚本,写2222’—‘我是第一个脚本’—‘我是文档。

如果你想让IE及以下的浏览器与其他浏览器保持一致的行为,可以把引入内部脚本的代码取出来,单独放入新的脚本标签中,因为document.write引入的代码的执行顺序必须在前一个标签中的代码后面。

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

通过动态脚本技术添加代码的主要目的是创建非阻塞脚本,因为通过动态脚本技术添加的代码不会立即执行,所以我们可以通过以下加载函数向页面添加动态脚本:

函数loadScript(url,回调){ var script=document . create element(' script ');script . type=' text/JavaScript ';//事件if(script . readystate){ script . onreadystatechange=function(){ if(script . readystate==' loaded ' | | script . readystate==' complete '){ callback callback();} } } else { script . onload=function(){ callbackcallback();} } script.src=urldocument . getelementsbytagname(' head ')[0]。appendChild(脚本);}但是,通过动态脚本技术添加的外部JavaScript脚本不能按照添加的顺序执行。这可以通过回调或者使用jQuery的html()方法来完成。详情请参阅://www . JB 51 . net/article/26446 . htm。

2.4通过Ajax注入脚本。

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

代码示例:

我们可以用下面的代码封装XMLHttpRequest对象,并封装它的get方法:

var xhr=(function(){ function createXhr(){ var xhr;if(窗口。XMLHttpRequest){ xhr=new XMLHttpRequest();}else if(窗口。ActiveX object){ var xhrVersions=[' MSXML 2。XMLHttp ',' MSXML2。XMLHttp.3.0 ',' MSXML2。XMLHttp.6.0'],I,lenfor(i=0,len=xhrVersions.length我透镜;I){ try { xhr=new ActiveX object(xhrerversons[I]);} catch(e){ } } else {抛出新错误(' xhr对象无法创建');}返回xhr}函数get(url,async,callback){ var xhr=createXhr();xhr . onreadystatechange=function(){ if(xhr . readystate==4){ if((xhr . status=200 xhr . status 300)| | xhr . status==304){ callbackcallback all back(xhr . responsetext);}else{alert('请求失败,错误代码为' xhr . status ');}}}xhr.open('get ',url,async);xhr . send(null);}返回{get:get}}()),然后基于xhr对象创建loadXhrScript函数:

函数loadXhrScript(url,async,回调){ if(async==undefined){ async=true;} xhr.get(url,async,function(text){ var script=document . create element(' script ');script . type=' text/JavaScript ';script.text=textdocument.body.appendChild(脚本);});}我们上面的get方法添加了一个参数,即是否异步,所以如果采用同步的方法,那么通过Ajax注入的脚本肯定会按照添加的顺序执行;相反,如果我们采用异步方案,添加脚本的执行顺序肯定是不确定的。

版权声明:浏览器执行中JavaScript脚本加载和代码执行顺序分析是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。