JAVASCRIPT 装载和执行

转载:http://coolshell.cn/articles/9749.htmljavascript

 

一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛,发现有很多的人对Javascript的执行和装载的基础并不懂,因此,从那天起我就想写一篇文章,但一直耽搁了。自上篇《浏览器渲染原理简介》,正好也能够承前启后。css

首先,我想说一下Javascript的装载和执行。一般来讲,浏览器对于Javascript的运行有两大特性:1)载入后立刻执行,2)执行时会阻塞页面后续的内容(包括页面的渲染、其它资源的下载)。因而,若是有多个js文件被引入,那么对于浏览器来讲,这些js文件被被串行地载入,并依次执行。html

由于javascript可能会来操做HTML文档的DOM树,因此,浏览器通常都不会像并行下载css文件并行下载js文件,由于这是js文件的特殊性形成的。因此,若是你的javascript想操做后面的DOM元素,基本上来讲,浏览器都会报错说对象找不到。由于Javascript执行时,后面的HTML被阻塞住了,DOM树时尚未后面的DOM结点。因此程序也就报错了。前端

传统的方式

因此,当你写在代码中写下以下的代码:java

1
2
< script type = "text/javascript"
         src = "http://coolshell.cn/asyncjs/alert.js" ></ script >

 

基本上来讲,head里的 <script>标签会阻塞后续资源的载入以及整个页面的生成。我专门作了一个示例你能够看看:示例一。 注意:个人alert.js中只有一句话:alert(“hello world”) ,这更容易让你看到javascript是怎么阻塞后面的东西的。程序员

因此,你知道为何有不少网站把javascript放在网页的最后面了,要么就是动用了window.onload或是docmuemt ready之类的事件。web

另外,由于绝大多数的Javascript代码并不须要等页面,因此,咱们异步载入的功能。那么咱们怎么异步载入呢?ajax

document.write方式

因而,你可能觉得document.write()这种方式可以解决不阻塞的方式。你固然会以为,document.write了的<script>标签后就能够执行后面的东西去了,这没错。对于在同一个script标签里的Javascript的代码来讲,是这样的,可是对于整个页面来讲,这个仍是会阻塞。 下面是一段测试代码:shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
< script type = "text/javascript" language = "javascript" >
     function loadjs(script_filename) {
         document.write('<' + 'script language="javascript" type="text/javascript"');
         document.write(' src="' + script_filename + '">');
         document.write('<'+'/script'+'>');
         alert("loadjs() exit...");
     }
 
     var script = 'http://coolshell.cn/asyncjs/alert.js';
 
     loadjs(script);
     alert("loadjs() finished!");
</ script >
 
< script type = "text/javascript" language = "javascript" >
    alert("another block");
</ script >

你以为alert的顺序是什么?你能够在不一样的浏览器里试一试。这里的想关的测试页面:示例二json

script的defer和async属性

IE自从IE6就支持defer标签,如:

1
2
< script defer type = "text/javascript" src = "./alert.js" >
</ script >

对于IE来讲,这个标签会让IE并行下载js文件,而且把其执行hold到了整个DOM装载完毕(DOMContentLoaded),多个defer的<script>在执行时也会按照其出现的顺序来运行。最重要的是<script>被加上defer后,其不会阻塞后续DOM的的渲染。可是由于这个defer只是IE专用,因此通常用得比较少。

而咱们标准的的HTML5也加入了一个异步载入javascript的属性:async,不管你对它赋什么样的值,只要它出现,它就开始异步加载js文件。可是, async的异步加载会有一个比较严重的问题,那就是它忠实地践行着“载入后立刻执行”这条军规,因此,虽然它并不阻塞页面的渲染,可是你也没法控制他执行的次序和时机。你能够看看这个示例去感觉一下

支持 async标签的浏览器是:Firefox3.6+,Chrome 8.0+,Safari 5.0+,IE 10+,Opera还不支持(来自这里)因此这个方法也不是太好。由于并非全部的浏览器你都能行。

动态建立DOM方式

这种方式多是用得最多的了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function loadjs(script_filename) {
     var script = document.createElement( 'script' );
     script.setAttribute( 'type' , 'text/javascript' );
     script.setAttribute( 'src' , script_filename);
     script.setAttribute( 'id' , 'coolshell_script_id' );
 
     script_id = document.getElementById( 'coolshell_script_id' );
     if (script_id){
         document.getElementsByTagName( 'head' )[0].removeChild(script_id);
     }
     document.getElementsByTagName( 'head' )[0].appendChild(script);
}
 
loadjs(script);

这个方式几乎成了标准的异步载入js文件的方式,这个方式的演示请参看:示例三。这方式还被玩出了JSONP的东东,也就是我能够为script的src指定某个后台的脚本(如PHP),而这个PHP返回一个javascript函数,其参数是一个json的字符串,返回来调用咱们的预先定义好的javascript的函数。你能够看一下这个示例:t.js (这个示例是我以前在微博征集的一个异步ajax调用的小例子

按需异步载入js

上面那个DOM方式的例子解决了异步载入Javascript的问题,可是没有解决咱们想让他按咱们指定的时机运行的问题。因此,咱们只须要把上面那个DOM方式绑到某个事件上来就能够了。

好比:

绑在window.load事件上——示例四 

你必定要比较一下示例四和示例三在执行上有什么不一样,我在这两个示例中都专门用了个代码高亮的javascript,看看那个代码高亮的的脚本的执行和个人alert.js的执行的状况,你就知道不一样了)

1
window.load = loadjs( "http://coolshell.cn/asyncjs/alert.js" )

绑在特定的事件上——示例五

1
< p style = "cursor: pointer" onclick = "LoadJS()" >Click to load alert.js </ p >

这个示例很简单了。当你点击某个DOM元素,才会真正载入咱们的alert.js。

更多

可是,绑定在某个特定事件上这个事彷佛又过了一点,由于只有在点击的时候才会去真正的下载js,这又会太慢了了。好了,到这里,要抛出咱们的终极问题——咱们想要异步地把js文件下载到用户的本地,可是不执行,仅当在咱们想要执行的时候去执行

要是咱们有下面这样的方式就行了:

1
2
3
4
5
6
7
var script = document.createElement( "script" );
script.noexecute = true ;
script.src = "alert.js" ;
document.body.appendChild(script);
 
//后面咱们能够这么干
script.execute();

惋惜的是,这只是一个美丽的梦想,今天咱们的Javascript还比较原始,这个“JS梦”尚未实现呢。

因此,咱们的程序员只能使用hack的方式来搞。

有的程序员使用了非标准的script的type来cache javascript。如:

1
< script type = cache /script src = "./alert.js" ></ script >

由于”cache/script”,这个东西根本就不能被浏览器解析,因此浏览器也就不能把alert.js当javascript去执行,可是他又要去下载js文件,因此就能够搞定了。惋惜的是,webkit严格符从了HTML的标准——对于这种不认识的东西,直接删除,什么也不干。因而,咱们的梦又破了。

因此,咱们须要再hack一下,就像N多年前玩preload图片那样,咱们能够动用object标签(也能够动用iframe标签),因而咱们有下面这样的代码:

1
2
3
4
5
6
7
8
function cachejs(script_filename){
     var cache = document.createElement( 'object' );
     cache.data = script_filename;
     cache.id = "coolshell_script_cache_id" ;
     cache.width = 0;
     cache.height = 0;
     document.body.appendChild(cache);
}

而后,咱们在的最后调用一下这个函数。请参看一下相关的示例:示例六

在Chrome下按 Ctrl+Shit+I,切换到network页,你就能够看到下载了alert.js可是没有执行,而后咱们再用示例五的方式,由于浏览器端有缓存了,不会再从服务器上下载alert.js了。因此,就能保证执行速度了。

关于这种preload这种东西你应该不会陌生了。你还可使用Ajax的方式,如:

1
2
3
var xhr = new XMLHttpRequest();
xhr.open( 'GET' , 'new.js' );
xhr.send( '' );

到这里我就再也不多说了,也不给示例了,你们能够本身试试去。

最后再提两个js,一个是ControlJS,一个叫HeadJS,专门用来作异步load javascript文件的。

好了,这是全部的内容了,但愿你们看事后能对Javascript的载入和执行,以及相关的技术有个了解。同时,也但愿各前端高手不吝赐教!

相关文章
相关标签/搜索