【转】JavaScript 的装载和执行
承接前面一篇文章《浏览器的渲染原理简介》 ,本文来说下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的装载和执行
前言 为什么要采用js来create一个script标签,设置src然后append到head中,而不是直接使用script标签,这样不是更简单点吗? javascript的装载和执行 首先,我想说一 ...
- Javascript 装载和执行(copy的感觉有很多错误。。)
copy from:http://coolshell.cn/articles/9749.html 首先,我想说一下Javascript的装载和执行.通常来说,浏览器对于Javascript的运行有两大 ...
- 关于JavaScript预编译和执行顺序以及函数引用类型的思考
昨晚在对项目中的一部分做模块化处理的时候,遇到了一个问题,一个重新定义的function对一个通用类中的function进行赋值覆盖的时候,失败了.问题抽象出来是这样的: <script > ...
- [转]Javascript中的自执行函数表达式
[转]Javascript中的自执行函数表达式 本文转载自:http://www.ghugo.com/javascript-auto-run-function/ 以下是正文: Posted on 20 ...
- 深入理解javascript中的立即执行函数(function(){…})()
投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014-06-12 我要评论 这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是 ...
- javascript从定义到执行 js引擎 闭包
javascript从定义到执行,JS引擎在实现层做了很多初始化工作,因此在学习JS引擎工作机制之前,我们需要引入几个相关的概念:执行环境 栈.全局对象.执行环境.变量对象.活动对象.作用域和作用域链 ...
- javascript 作用域链与执行环境
前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 作用域.作用域链.执行环境.执行环境栈以及this的概念在javascript中非常重要,本人经常弄混淆, ...
- 怎么样加快JavaScript加载和执行效率
概览 无论当前 JavaScript 代码是内嵌还是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成.JavaScript 执行过程耗时越久,浏览器等待响应用户输入的时间就越长.浏览器在下载 ...
- javascript中的立即执行函数(function(){…})()
javascript中的立即执行函数(function(){…})() 深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是用(function(){…})()包 ...
随机推荐
- poj3585 树形dp 二次扫描,换根法模板题
#include<iostream> #include<cstring> #include<cstdio> #include<vector> using ...
- python 全栈开发,Day121(DButils,websocket)
昨日内容回顾 1.Flask路由 1.endpoint="user" # 反向url地址 2.url_address = url_for("user") 3.m ...
- 2018-2019-2 网络对抗week1 Kali安装 20165333陈国超
Kali安装 安装过程是按照网上的教程装的,链接点击[https://blog.csdn.net/KNIGH_YUN/article/details/79949512] 安装成功的截图 主要说一下安装 ...
- 《剑指offer》-栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一 ...
- ERP产品采购申请管理(三十八)
BLL层代码: public class BioPurchaseBLL { /// <summary> /// 购进申请添加 /// </summary> /// <pa ...
- 国内最火5款Java微服务开源项目
目录 1.pig 2.zheng 3.Cloud-Platform 4.SpringBlade 5.Guns 1.pig 开源地址:https://gitee.com/log4j/pig 基于Spri ...
- POJ 1742 Coins 【多重背包DP】
题意:有n种面额的硬币.面额.个数分别为A_i.C_i,求最多能搭配出几种不超过m的金额? 思路:dp[j]就是总数为j的价值是否已经有了这种方法,如果现在没有,那么我们就一个个硬币去尝试直到有,这种 ...
- 《Gradle权威指南》--Gradle构建脚本基础
No1: 设置文件默认名是setting.gradle,放在根目录下,大多数作用都是为了配置子工程 No2: 一个Project包含很多个Task.Task就是一个操作,一个原子性的操作.其实它是Pr ...
- hdu 1394 (线段树求逆序数)
<题目链接> 题意描述: 给你一个有0--n-1数字组成的序列,然后进行这样的操作,每次将最前面一个元素放到最后面去会得到一个序列,那么这样就形成了n个序列,那么每个序列都有一个逆序数,找 ...
- 只有在配置文件或 Page 指令中将 enableSessionState 设置为 true 时
HttpContext context = HttpContext.Current; //而后,再使用: if (context.Session["user"] != null) ...