可以看到,在某个正常脚本加载时间较长的时候,动态脚本的执行明显提前,无论在IE还是CHROME、firefox和opera中,均在DOMContentLoaded之前执行,因此我们可以初步判断,动态脚本的执行时机是不确定的,在不同浏览器的执行时机也都存在差异,但总的来看应该是在代码加载结束之后,并且线程中没有JavaScript代码执行的某个时间,但不同浏览器对这个时间有着不同的把握。
因此,动态脚本是否会阻塞DOMContentLoaded也是不确定的,因为动态脚本可能在DOMContentLoaded触发之前,也可能在触发之后执行。而且由于IE<=8不支持真正的DOMContentLoaded事件,jQuery在IE<=8中也是模拟判断该事件的发生(下一篇会专门讲解DOMContentLoaded事件),一定程度上也会对我们上述代码的执行结果造成影响。
1.1.3 DEMO3:动态脚本与defer
我们知道,defer脚本是有着相对明确的执行时机的,即页面解析完成之后,DOMContentLoaded触发之前加载并且执行,事实上,二者之间在执行时机上并不存在什么关联,但是在实际实验中发现,动态脚本可能会在defer脚本之前或者之后执行,但却不会打断defer脚本的执行,我们再引入《浏览器环境下JavaScript脚本加载与执行探析之defer与async特性》中2.3节的DEMO中的defer脚本,修改HTML代码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-"/> <title>Dynamic Script Test</title> <script src="https://lib.sinaapp.com/js/jquery/../jquery-...min.js"></script> <script src="https://www.jb51.net/util.js"></script> <script> $(function(){ test += "我是DOMContentLoaded里面的脚本\n"; }) window.onload = function(){ test += "我是window.onload里面的脚本\n"; var button = document.getElementById("test"); button.onclick = function(){ console.log(test); } } </script> <script type="text/javascript">var test = "";</script> <script> loadScript("dynamic.js"); </script> <script> test += "我是head内部脚本\n"; </script> <script src="https://www.jb51.net/defer.js" type="text/javascript" defer="defer"></script> <script src="https://www.jb51.net/article/.js" type="text/javascript"></script> </head> <body> <button>点击一下</button> <script> loadScript("dynamic.js"); </script> <script src="https://www.jb51.net/defer.js" type="text/javascript" defer="defer"></script> <script src="https://www.jb51.net/article/.js" type="text/javascript"></script> </body> <script> loadScript("dynamic.js"); </script> <script src="https://www.jb51.net/defer.js" type="text/javascript" defer="defer"></script> <script src="https://www.jb51.net/article/.js" type="text/javascript"></script> </html>
注:firefox和opera中执行结果可能会变化
我们增加了几个defer的脚本,再来看一下各个浏览器中的执行结果:
从实验结果可以看出,动态脚本的执行时机与defer脚本并没有直接的关系,表面上看起来在CHROME和firefox中,延迟脚本总是在动态脚本之前执行,在《前端优化-Javascript篇(2.异步加载脚本)》一文中提到过“ScriptDOM和defer同时都可以执行,在不同浏览器中它们的优先级的不一样的。在Firfox和Chrome中,ScriptDOM的优先级比defer低,而在IE中情况则相反。”,其实这种优先级应该是不存在的,我们只需要将defer脚本加一个加载延迟,那么动态脚本的执行就会先于defer脚本了。
1.2 动态脚本执行问题总结
我们再来总结一下动态脚本的执行问题:
[1]首先,动态脚本确实能够在一定程度上起到延迟脚本执行的作用,但由于动态脚本的执行时机的不确定性,这种延迟作用的效果也是未知的。
[2]其次,动态脚本的执行顺序不一定会按照添加的顺序,这是动态脚本技术比较大的问题之一,最简单的解决方式就是使用回调函数,监听脚本的加载状态,在一个脚本加载结束后再动态添加下一个脚本。
[3]动态脚本没有确切的执行时机,当通过DOM的appendChild、insertBefore等方法将script元素添加到DOM中时,就会去加载JS脚本,脚本的执行应该是在加载结束后的某个时机,不同浏览器对这个时机的处理差异比较大,比如在IE中,应该是采取尽快执行的策略,也就是在加载结束后尽快寻找时机执行代码