承接前面一篇文章《浏览器的渲染原理简介》 ,本文来说下JavaScript的装载和执行。

通常来说,浏览器对于 JavaScript 的运行有两大特性:

1) 载入后马上执行

2) 执行时会阻塞页面后续的内容(包括页面的渲染、其他资源的下载)

所以,如果有多个JS文件被引入,那么对于浏览器来说,这些JS文件将被串行地载入并依次执行。

由于JavaScript 可能会操作 HTML文档的DOM 树,所以浏览器一般都不会像并行下载CSS文件一样并行下载JS文件,这是JS文件的特殊性造成的。因此,如果你的JavaScript想操作后面的DOM 元素,浏览器会报错说找不到对象,这是因为JavaScript执行时后面的HTML被阻塞住了,操作DOM 树时还没有后面的节点。

传统方式

当你写下如下代码时:

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

基本上来说,head里的<script>标签会阻塞后续资源的载入以及整个页面的生成。比如上面这个示例,其中只有一句JS代码(示例):

alert(“hello world”) ;

效果是在加载此JS文件时会弹出一个对话框,因此点击这个对话框后才会对后续资源进行载入以及对整个页面的进行生成。

所以,有很多网站会把 js 放在网页的最后面,或者使用 window.load、$(document).ready(function(){}) 之类的事件。

另外,由于绝大多数的JavaScript代码并不需要等待页面,我们需要异步载入功能。那我们怎么异步载入呢?

document.write 方式

你可能以为 document.write() 方式能够解决不阻塞的方式。通过 document.write 方法写入<script>标签的方式就可以执行后面的东西,对于在同一个 script 标签内的 JS代码来说是这样的。但是,对于整个页面来说,还是会阻塞的。下面是一段测试代码(示例):

<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>

依此弹出的对话框为:

loadjs() exit...
loadjs() finished!
hello world
another block

然后才会显示页面。

script 的defer和async属性

IE自从IE6就之处defer 标签,如:

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

对于IE来说,这个标签会让IE并行下载JS文件,并且把其执行hold到了整个DOM装载完毕,多个defer 的<script>在执行时也会按照其出现的顺序来运行。最重要的是<script>被加上refer 后,其不会阻塞后续DOM 的渲染。但是因为refer 只是IE专用,所以一般用的比较少。

我们的HMTL 5也加入了一个异步载入 JavaScript的属性:async 。无论你对它赋什么样的值,只要它出现,它就开始异步加载 JS文件。但是,async的异步加载会有一个比较严重的问题,那就是它忠实的执行“载入后马上执行”这条规则。所以,虽然它并不阻塞页面的渲染,但是你也无法控制他执行的次序和时机(示例)。

支持 async 标签的浏览器如下,Opera还不支持(来自这里),所以这个方法也不是太好。

动态创建DOM的方式

这种方式可能是用的最多的了。

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);
} var script = 'http://coolshell.cn/asyncjs/alert.js';
loadjs(script);

这种方式几乎成了标准的异步载入js文件的方式(示例)。这种方式还玩出了 jsonp 的东东。也就是我们可以为script的src 指定某个后台的脚本(比如PHP),而这个PHP返回一个JavaScript函数,其参数是一个json 字符串,返回来调用我们预先定义好的 JavaScript 函数。作者的参考示例:t.js  (这个示例是作者之前在微博征集的一个异步ajax调用的小例子

按需异步载入JS

上面的DOM方式的例子解决了异步载入JavaScript的问题,但是没有解决我们想让他按我指定的时机运行的问题。所以,我们需要把上面的DOM方式绑定到某个事件上就可以了。

比如:

1) 绑在window.load 事件上(示例

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

2) 绑在特定的事件上(示例

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

比如当我们在点击某个DOM元素时,才载入我们的JS文件。

更多

有的人可能会觉得绑定在某个特定事件上似乎过了一点,而在点击时才载入JS又太慢了。这里抛出一个终极问题:我们想要异步地把JS文件下载到用户本地,但是又不执行,仅当我们想要执行的时候才去执行。

作者提出了一种方式,就像多年之前玩preload图片那样,我们可以动用 object 标签(也可以使用 iframe 标签),于是有了下面的代码(示例):

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 下按F12(或者Ctrl+Shit+I),切换到 network页,可以看到 alert.js 文件已经下载了但是却没有执行弹出 "hello,world"对话框的操作。然后我们再用之前“绑在特定的事件上”的方式,因为浏览器端有缓存了,不会在从服务器上下载 alert.js 文件了,这样就能保证执行速度了。

我们还可以用Ajax的方式,比如:

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

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

来源:JavaScript 的装载和执行

【转】JavaScript 的装载和执行的更多相关文章

  1. javascript的装载和执行

    前言 为什么要采用js来create一个script标签,设置src然后append到head中,而不是直接使用script标签,这样不是更简单点吗? javascript的装载和执行 首先,我想说一 ...

  2. Javascript 装载和执行(copy的感觉有很多错误。。)

    copy from:http://coolshell.cn/articles/9749.html 首先,我想说一下Javascript的装载和执行.通常来说,浏览器对于Javascript的运行有两大 ...

  3. 关于JavaScript预编译和执行顺序以及函数引用类型的思考

    昨晚在对项目中的一部分做模块化处理的时候,遇到了一个问题,一个重新定义的function对一个通用类中的function进行赋值覆盖的时候,失败了.问题抽象出来是这样的: <script > ...

  4. [转]Javascript中的自执行函数表达式

    [转]Javascript中的自执行函数表达式 本文转载自:http://www.ghugo.com/javascript-auto-run-function/ 以下是正文: Posted on 20 ...

  5. 深入理解javascript中的立即执行函数(function(){…})()

    投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014-06-12 我要评论 这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是 ...

  6. javascript从定义到执行 js引擎 闭包

    javascript从定义到执行,JS引擎在实现层做了很多初始化工作,因此在学习JS引擎工作机制之前,我们需要引入几个相关的概念:执行环境 栈.全局对象.执行环境.变量对象.活动对象.作用域和作用域链 ...

  7. javascript 作用域链与执行环境

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 作用域.作用域链.执行环境.执行环境栈以及this的概念在javascript中非常重要,本人经常弄混淆, ...

  8. 怎么样加快JavaScript加载和执行效率

    概览 无论当前 JavaScript 代码是内嵌还是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成.JavaScript 执行过程耗时越久,浏览器等待响应用户输入的时间就越长.浏览器在下载 ...

  9. javascript中的立即执行函数(function(){…})()

    javascript中的立即执行函数(function(){…})() 深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是用(function(){…})()包 ...

随机推荐

  1. 开始写博客,学习Linq(3)

    为什么需要Linq?(摘自原文) 读者会发现LINQ着眼于解决编程语言和数据库之间广发存在的不统一问题. 1.常见的问题,使用.NET Framework Class Library(FCL)提供了A ...

  2. [转] 理解 JavaScript 中的 Array.prototype.slice.apply(arguments)

    假如你是一个 JavaScript 开发者,你可能见到过 Array.prototype.slice.apply(arguments) 这样的用法,然后你会问,这么写是什么意思呢? 这个语法其实不难理 ...

  3. 交叉编译和安装ARM板(RK3288)和Linux 3.10上的RTL8188无线网卡驱动

    插入无线网卡,输入ifconfig,发现没有检测到网卡. 输入lsusb,查看无线网卡型号. 我用的无线网卡是EDUP的网卡,包装盒里有一张驱动光盘,把光盘里linux下的驱动目录复制下来.如果没有驱 ...

  4. PLSQL程序设计(Oracle)

    Hello World set serveroutput on; declare --说明部分 begin --程序 dbms_output.put_line('Hello World'); end; ...

  5. asp.net core web项目目录解读

    Connected Services 和传统.net web项目相比,它的功能类似于添加webservice或者wcf service的引用.暂时用不到,有兴趣的小伙伴可以深入了解.右键这个目录可以看 ...

  6. 让我们了解 Ceph 分布式存储

    前言 最近在学习 kubernetes 过程中,想实现 pod 数据的持久化.在调研的过程中,发现 ceph 在最近几年发展火热,也有很多案例落地企业.在选型方面,个人更加倾向于社区火热的项目,Glu ...

  7. C# 反编译防范

    C# 编写的代码通过VS编译器生成 dll 或 exe ,很容易被一些反编译工具查看到源码或对源码进行修改.为防止代码被反编译或被篡改,我们可以进行一定的防范措施.但不能杜绝,因为DotNet编写代码 ...

  8. Python3练习题系列(08)——代码阅读方法及字典跳转表理解

    问题:分析下面代码 cities['_find'] = find_city city_found = cities['_find'](cities, state) 分析过程: 一个函数也可以作为一个变 ...

  9. Python3练习题系列(06)——各种符号总结

    Python3中的各种符号总结 1关键字 import keyword print(keyword.kwlist, end='\t') ['False', 'None', 'True', 'and', ...

  10. docker测试时候命令无法补全的解决方法_docker

    发现问题 在输入docker swarm 然后tab键不能像这样进行提示 和补全 tab 键也无法补全nginx容器名,下面是运行截图 解决方法: yum install -y bash-comple ...