javascript 文件的同步加载与异步加载实现原理


Posted in Javascript onDecember 13, 2012

HTML 4.01 的script属性
charset: 可选。指定src引入代码的字符集,大多数浏览器忽略该值。
defer: boolean, 可选。延迟脚本执行,相当于将script标签放入页面body标签的底部,js脚本会在document的DOMContentLoaded之前执行。除IE和较新版本的Firefox外,其他浏览器并未支持。
language: 已废弃。大部分浏览器会忽略该值。
src: 可选。指定引入的外部代码文件,不限制后缀名。
type: 必选。指定脚本的内容类型(MIME类型)。现实中通常不指定该值也可以,浏览器会默认当作text/javascript类型来解释执行。

HTML5中的script属性
script 标签在HTML5中除了具备HTML5新标准定义的属性以外,和HTML4.01相比移除了language属性,修改了type属性为可选的(默认text/javascript),并新增了一个属性async。
async :boolean, 属性的作用,定义脚本是否异步执行,取值true或false。
如果 async 设为 true ,会忽略 defer 属性。
异步执行的 js 文件被假定为不使用 document.write() 向加载中的 document 写入内容,因此不要在 异步执行的 js 文件的加载执行过程中使用 document.write()
除了 script 标签属性外,页面引入 js 文件的方式影响其加载执行方式:
任何以添加 script 节点(例如 appendChild(scriptNode) ) 的方式引入的js文件都是异步执行的 (scriptNode 需要插入document中,只创建节点和设置 src 是不会加载 js 文件的,这跟 img 的预加载不能类比 )
html文件中的<script>标签中的代码或src引用的js文件中的代码是同步加载和执行的
html文件中的<script>标签中的代码使用document.write()方式引入的js文件是异步执行的
html文件中的<script>标签src属性所引用的js文件的代码内再使用document.write()方式引入的js文件是同步执行的
使用 Image 对象异步预加载 js 文件(不会被执行)

不要使用类似下面这种做法,这样并不会发起加载 js 文件的请求:
divNode.innerHTML = '<script src="xxx.js"></script>';
window.onload 事件会在 js 文件加载完毕才触发(即使是异步加载)
=====================================================
1、
<script>
//同步加载执行的代码
</script>
2、
<script src="xx.js"></script> //同步加载执行xx.js中的代码
3、
<script>
document.write('<script src="xx.js"><\/script>'); //异步加载执行xx.js中的代码
</script>
4、
<script src="xx.js"></script>
xx.js中有下面代码:

document.write('<script src="11.js"><\/script>'); 
document.write('<script src="22.js"><\/script>');

则xx.js和11.js、22.js 都是同步加载和执行的。
如果 xx.js 、11.js 和 22.js 以插入 script 节点方式异步加载,则 11.js 和 22.js 是异步加载的,
如果 xx.js 以script 节点方式异步加载, 11.js 和 22.js 以 document.write(script) 方式加载,则 11.js 和 22.js 是同步加载的(经最新的浏览器测试, 在chrome 下,xx.j 异步加载执行已经无法使用 document.write() 向文档写入内容的 ,但是 firefox 和IE 却可以在 document 关闭之前写入(方法是在 html 中alert阻止文档关闭))
测试:在11.js中 alert()(不要用 for 循环,浏览器是单线程执行的,持续执行任何一段代码都会导致其余代码被阻塞) , 22.js 中 console.log() ,可以看到 22.js中的代码被阻塞
5、
下面这种方式,xx.js会在appendChild执行之后异步加载执行
var script = document.createElement("script"); 
script.setAttribute("src","xx.js"); 
documenrt.getElementsByTagName("head")[0].appendChild(script);

6、使用 Image 对象异步预加载 js 文件(不会被执行)
Image 的 src 被赋值时即发起请求,而且对文件类型不挑剔(图片也可能是有脚本动态创建的,比如验证码),因此可以将 js 文件的 url 赋给 image.src, js 加载之后被浏览器缓存.
var img = new Image(); 
img.onload = function(){ alert(1); } ; //由于返回的js文件 MIME 不是图片,onload回调函数并不会被触发 
img.src = 'http://localhost/test/loadjs/try.2.js'; 
var s = document.createElement("script"); 
var h = document.getElementsByTagName("head")[0]; 
//执行 js 
s.src=img.src; 
h.appendChild(s);

一个加载 js 文件的 函数:
var loadJS = function(url,callback){ 
var head = document.getElementsByTagName('head'); 
if(head&&head.length){ 
head = head[0]; 
}else{ 
head = document.body; 
} 
var script = document.createElement('script'); 
script.src = url; 
script.type = "text/javascript"; 
head.appendChild( script); 
script.onload = script.onreadystatechange = function(){ 
//script 标签,IE 下有 onreadystatechange 事件, w3c 标准有 onload 事件 
//这些 readyState 是针对IE8及以下的,W3C 标准的 script 标签没有 onreadystatechange 和 this.readyState , 
//文件加载不成功 onload 不会执行, 
//(!this.readyState) 是针对 W3C标准的, IE 9 也支持 W3C标准的 onload 
if ((!this.readyState) || this.readyState == "complete" || this.readyState == "loaded" ){ 
callback(); 
} 
}//end onreadystatechange 
}

对于第4点的测试(同步加载)(其中插入 alert 很容易看到加载时的阻塞)
tryjs.html
<!DOCTYPE html> 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<script src="tryjs.js" 
onload="if(!document.all){console.log('outer js callback, not IE');}" 
onreadystatechange="console.log('outer js callback ',this.readyState,' IE');"></script> 
<body> 
</body> 
</html>

tryjs.js
console.log('write begin'); 
document.write('<script src="try.1.js" onreadystatechange="console.log(\'file 1 callback \',this.readyState,\' IE\');" onload="if(!document.all){console.log(\'file 1 callback,NOT IE \');}"><\/script>'); 
document.write('<script src="try.2.js" onreadystatechange="console.log(\'file 2 callback \',this.readyState,\' IE\');" onload="if(!document.all){console.log(\'file 2 callback,NOT IE \');}"><\/script>'); 
console.log('write finished');

try.1.js
console.log('loadjs 1 begin'); 
console.log('loadjs 1 finished');

try.2.js
console.log('loadjs 2 begin'); 
console.log('loadjs 2 finished');

测试结果(file 2 和 file 1 的 callback complete 在IE7\8\9次序不确定)
IE 7:
日志: outer js callback loading IE
日志: outer js callback loaded IE
日志: write begin
日志: write finished
日志: outer js callback complete IE
日志: file 1 callback loading IE
日志: file 2 callback loading IE
日志: loadjs 1 begin
日志: loadjs 1 finished
日志: loadjs 2 begin
日志: loadjs 2 finished
日志: file 2 callback complete IE
日志: file 1 callback complete IE

IE8:
日志: outer js callback loading IE
日志: outer js callback loaded IE
日志: write begin
日志: write finished
日志: outer js callback complete IE
日志: file 1 callback loading IE
日志: file 2 callback loading IE
日志: loadjs 1 begin
日志: loadjs 1 finished
日志: loadjs 2 begin
日志: loadjs 2 finished
日志: file 2 callback complete IE
日志: file 1 callback complete IE

IE9:
日志: write begin
日志: write finished
日志: outer js callback complete IE
日志: file 1 callback loading IE
日志: file 2 callback loading IE
日志: loadjs 1 begin
日志: loadjs 1 finished
日志: loadjs 2 begin
日志: loadjs 2 finished
日志: file 1 callback complete IE
日志: file 2 callback complete IE

FIREFOX:
write begin
write finished
outer js callback, not IE
loadjs 1 begin
loadjs 1 finished
file 1 callback,NOT IE
loadjs 2 begin
loadjs 2 finished
file 2 callback,NOT IE

CHROME:
write begin
write finished
outer js callback, not IE
loadjs 1 begin
loadjs 1 finished
file 1 callback,NOT IE
loadjs 2 begin
loadjs 2 finished
file 2 callback,NOT IE

Javascript 相关文章推荐
Javascript实现的分页函数
Feb 07 Javascript
基于JQuery的动态删除Table表格的行和列的代码
May 12 Javascript
深入理解javascript变量声明
Nov 20 Javascript
基于javascript实现表格的简单操作
May 21 Javascript
jquery操作checkbox火狐下第二次无法勾选的解决方法
Oct 10 Javascript
JS实现颜色梯度与渐变效果完整实例
Dec 30 Javascript
详解Vue自定义过滤器的实现
Jan 10 Javascript
layer弹出层中H5播放器全屏出错的解决方法
Feb 21 Javascript
Bootstrap fileinput文件上传预览插件使用详解
May 16 Javascript
在JavaScript中如何访问暂未存在的嵌套对象
Jun 18 Javascript
vue移动端的左右滑动事件详解
Jun 17 Javascript
基于脚手架创建Vue项目实现步骤详解
Aug 03 Javascript
javaScript复制功能调用实现方案
Dec 13 #Javascript
js遍历td tr等html元素
Dec 13 #Javascript
js关闭模态窗口刷新父页面或跳转页面
Dec 13 #Javascript
HTML复选框和单选框 checkbox和radio事件介绍
Dec 12 #Javascript
自己写的兼容ie和ff的在线文本编辑器类似ewebeditor
Dec 12 #Javascript
用javascript模仿ie的自动完成类似自动完成功的表单
Dec 12 #Javascript
javascript实现图片切换的幻灯片效果源代码
Dec 12 #Javascript
You might like
php 常用类整理
2009/12/23 PHP
解析PHP中如何将数组变量写入文件
2013/06/06 PHP
使用PHP函数scandir排除特定目录
2014/06/12 PHP
30个php操作redis常用方法代码例子
2014/07/05 PHP
jquery获取多个checkbox的值异步提交给php的方法
2015/06/24 PHP
php实现支持中文的文件下载功能示例
2017/08/30 PHP
JQuery 学习技巧总结
2010/05/21 Javascript
为指定的元素添加遮罩层的示例代码
2014/01/15 Javascript
纯JS实现可拖拽表单的简单实例
2016/09/02 Javascript
angular.js 路由及页面传参示例
2017/02/24 Javascript
在React中如何优雅的处理事件响应详解
2017/07/24 Javascript
vue车牌号校验和银行校验实战
2019/01/23 Javascript
浅析vue中的MVVM实现原理
2019/03/04 Javascript
实例分析Array.from(arr)与[...arr]到底有何不同
2019/04/09 Javascript
微信小程序在ios下Echarts图表不能滑动的问题解决
2019/07/10 Javascript
微信小程序前端promise封装代码实例
2019/08/24 Javascript
Python 时间操作例子和时间格式化参数小结
2014/04/24 Python
10个易被忽视但应掌握的Python基本用法
2015/04/01 Python
深入理解Python中字典的键的使用
2015/08/19 Python
idea创建springMVC框架和配置小文件的教程图解
2018/09/18 Python
python求质数的3种方法
2018/09/28 Python
python字典嵌套字典的情况下找到某个key的value详解
2019/07/10 Python
python mqtt 客户端的实现代码实例
2019/09/25 Python
Python内存映射文件读写方式
2020/04/24 Python
pytorch判断是否cuda 判断变量类型方式
2020/06/23 Python
matplotlib绘制多子图共享鼠标光标的方法示例
2021/01/08 Python
css3简单练习实现遨游浏览器logo的绘制
2013/01/30 HTML / CSS
香港礼品网站:GiftU eshop
2017/09/01 全球购物
外贸学院会计专业应届生求职信
2013/11/14 职场文书
优秀团员个人事迹材料
2014/01/29 职场文书
工程师岗位职责规定
2014/02/26 职场文书
中国梦我的梦演讲稿
2014/04/23 职场文书
产品销售计划书
2014/05/04 职场文书
国际经济贸易专业自荐信
2014/06/13 职场文书
字典算法实现及操作 --python(实用)
2021/03/31 Python
浅谈Redis缓冲区机制
2022/06/05 Redis