传统同步加载
//同步加载
<script type="text/JavaScript" src="index.js"></script>
defer实现
添加 defer 属性就可以变成异步,但 不会 立即执行
等待DOM解析完毕才会执行
(IE8-IE4都支持)
//异步加载 会等待DOM树加载完毕再执行
<script type="text/JavaScript" src="index.js" defer="defer"></script>
async实现
与上面恰恰相反,只要该链接文件加载完毕,会立即执行
不论DOM是否解析完毕
(W3C H5属性 IE9及以上支持)
当defer和async同时出现在一起,除了IE外一律只认async(如高版本IE也是认async但低版本会认defer,firefox也是认async)
不建议异步加载写DOM操作有关的,一般都是封装的函数且与DOM操作无关
//加载完就会执行
<script type="text/JavaScript" src="index.js" async="async"></script>
生成异步script标签
<script type="text/JavaScript">
var s = document.createElement('script');
s.type='text/javascript';
// s.async=true;
s.src='index.js';
document.body.appendChild(s);
//这种写法虽然是异步的但是会阻塞onload方法
</script>
//用下面的写法就可以实现onload过后再进行异步的加载
<script type="text/JavaScript">
function async_load() {
var s = document.createElement('script');
s.type='text/javascript';
// s.async=true;
s.src='index.js';
document.body.appendChild(s);
}
if (window.attachEvent) {
window.attchEvent('onload',async_load);//(IE)
}else{
window.addEventListener('load',async_load,false);
}
</script>
//最好写成立即执行函数,成为一个独立的模块
<script type="text/JavaScript">
(function () {
function async_load() {
var s = document.createElement('script'),
//将这个生成的script添加到最上面
t = document.getElementsByTagName('script')[0]
;
s.type='text/javascript';
// s.async=true;//可写可不写这样生成的已经是异步的了
s.src='index.js';
// document.body.appendChild(s);
t.parentNode.insertBefore(s, t);//将这个生成的script添加到最上面
}
if (window.attachEvent) {
window.attchEvent('onload',async_load);
}else{
window.addEventListener('load',async_load,false);
}
})();
</script>
//在s.src的时候已经开始下载这个对应的文件了,当动态创建的script放入HTML里时就已经执行了
案例
此时有一个 utils.js ,既要异步的去加载这个文件还要在加载完成后调用文件里的 test1 和 test2 方法
在开头和结尾同步的调用的 utils2.js 里的 test3 来观察它们的执行顺序
写一个 utils.js(该文件和下面HTML在同级目录)
var utils ={
test1:function () {
console.log('test1');
},
test2:function () {
console.log('test2');
},
}
再写一个utils2.js(也在一个目录)
(function () {
console.log('test3');
})()
随便写一个html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!-- 调用了test3 -->
<script src="utils2.js"></script>
</head>
<body>
<div class="div">1</div>
<div class="div">2</div>
<div class="div">3</div>
<div class="div">4</div>
<div class="div">5</div>
<div class="div">6</div>
<div class="div">7</div>
<div class="div">8</div>
<div class="div">9</div>
<div class="div">10</div>
<script>
console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);
console.log(6);
console.log(7);
console.log(8);
console.log(9);
console.log(10);
utils_test1_('utils.js', 'test1');//异步调用test1
//注意这里有一个定时器
setTimeout(function () { console.log(11); }, 0);
utils_test1_('utils.js', 'test2');//异步调用test2
//生成script标签函数
function utils_test1_(url, fn) {
var s = document.createElement('script'),
Oscript = document.getElementsByTagName('script')[0];
s.type = 'text/javascript';
// s.src = url;
if (s.readyState) {//IE
s.onreadystatechange = function () {
if (s.readyState == 'loaded' || s.readyState == 'complete') {
utils[fn]();
}
};
} else {//非IE
s.onload = function () {
utils[fn]();
};
}
//url无论放在这里还是上面都可以,因为readyState、onload是一直在监听的
// 直到url下载完成后就会执行相对应的utils[fn]();
s.src = url;
Oscript.parentNode.insertBefore(s, Oscript);
}
console.log(12);
console.log(13);
console.log(14);
console.log(15);
console.log(16);
console.log(17);
console.log(18);
console.log(19);
console.log(20);
console.log(21);
console.log(22);
console.log(23);
console.log(24);
console.log(25);
console.log(26);
console.log(27);
console.log(28);
console.log(29);
console.log(30);
</script>
<!-- 调用了test3 -->
<script src="utils2.js"></script>
</body>
</html>
观察打印的结果就可以看到 test1 和 test2 的打印并没有阻塞console.log按顺序打印
而且你不断的刷新后会发现 有时 test1 在前, test2 在后,有时 test2 在前, test1 在后面,取决于谁先加载完
但你可以看到 test3 是按照同步加载的,而且是 早于定时器 的(挖坑)