我们通常会认为,在 html 中,位置靠前的 js,会优先加载、执行。这句话大部分情况下正确,但是随着浏览器的升级调整,新特性的加入,这个加载、执行顺序也越来越复杂。
首先我们看一个最简单的情况
- <scriptsrc="a.js"></script>
- <scriptsrc="b.js"></script>
chrome 下,为了减少等待时间,a,b 会并行加载,但是执行时则会先 a 后 b;并不是 a 加载,a 执行,b 加载,b 执行的顺序。
关于 async、defer 功能及异同的介绍: 点击前往
所以如果代码是这样的
- <scriptsrc="a.js"async></script>
- <scriptsrc="b.js"></script>
a,b 同样并行加载,但此时如果是 b 先加载完,则 b 会优先执行;
例:
- <scripttype="text/javascript">
- var snode = document.createElement('script');
- snode.src = 'a.js';
- document.head.appendChild(snode);
- </script>
- <scripttype="text/javascript">
- var snodeb = document.createElement('script');
- snodeb.src = 'b.js';
- document.head.appendChild(snodeb);
- </script>
对于支持 async 属性的浏览器,动态插入的外链脚本, 相当于默认具有 async=true;也就是 a.js, b.js 执行顺序不确定的;
如果需要让动态插入的脚本按插入顺序执行,可以显式设置 async = false;
- <scripttype="text/javascript">
- var snode = document.createElement('script');
- snode.src = 'a.js';
- snode.async = false;
- document.head.appendChild(snode);
- </script>
- <scripttype="text/javascript">
- var snodeb = document.createElement('script');
- snodeb.src = 'b.js';
- snodeb.async = false;
- document.head.appendChild(snodeb);
- </script>
特殊场景下,我们会使用 js 动态往页面插入脚本,比如:
- <scripttype="text/javascript">
- var dn = document.createElement('script');
- dn.text = 'console.log("dn")';
- document.head.appendChild(dn);
- </script>
此动态插入的脚本并不会执行;
但是如果我们换一种方式,将其改成外链,但是 url 用一个 data url 来代替,则可以执行,比如:
- <scripttype="text/javascript">
- var dn = document.createElement('script');
- dn.src='data:application/javascript,' + 'console.log("dn")';
- document.head.appendChild(dn);
- </script>
例:
- <scripttype="text/javascript">
- function track(evt){
- console.log(evt.type +' : '+ evt.target.src);
- }
- </script>
- <scriptsrc="c.js"onload="track(event)"></script>
执行后可以看出 js 加载完成后会优先完成解析,执行,然后才触发 onload 事件。内联脚本没有 onload 事件触发。如果我们要动态加载一个脚本,并在其加载执行完成后有回调,onload 就可以派上用场了。
来源: http://www.tuicool.com/articles/bim2uyf