1. 浏览器加载
- 传统方法
<script>标签打开defer
或async
属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
defer
与async
的区别是:defer
要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async
一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer
是“渲染完再执行”,async
是“下载完就执行”。另外,如果有多个defer
脚本,会按照它们在页面出现的顺序加载,而多个async
脚本是不能保证加载顺序的。 - 加载规则
浏览器加载 ES6 ??椋彩褂?code><script>标签,但是要加入type="module"
属性。
<script type="module" src="./foo.js"></script>
浏览器对于带有type="module"
的<script>
,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>
标签的defer
属性。如果网页有多个<script type="module">
,它们会按照在页面出现的顺序依次执行。
一旦使用了async
属性,<script type="module">
就不会按照在页面出现的顺序执行,而是只要该??榧釉赝瓿桑椭葱懈媚??。
对于外部的模块脚本(上例是foo.js),有几点需要注意。
代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。
??榻疟咀远捎醚细衲J?,不管有没有声明use strict
。
??橹校梢允褂?code>import命令加载其他??椋?code>.js后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用export
命令输出对外接口。
??橹校ゲ愕?code>this关键字返回undefined
,而不是指向window
。也就是说,在模块顶层使用this
关键字,是无意义的。
同一个模块如果加载多次,将只执行一次。
2. ES6 ??橛?CommonJS ??榈牟钜?/h4>
它们有两个重大差异。
CommonJS ??槭涑龅氖且桓鲋档目奖?,ES6 ??槭涑龅氖侵档囊谩?br> CommonJS ??槭窃诵惺奔釉兀珽S6 ??槭潜嘁胧笔涑鼋涌凇?/p>
3. Node 加载
Node 对 ES6 ??榈拇肀冉下榉?,因为它有自己的 CommonJS 模块格式,与 ES6 ??楦袷绞遣患嫒莸摹D壳暗慕饩龇桨甘?,将两者分开,ES6 ??楹?CommonJS 采用各自的加载方案。
Node 要求 ES6 ??椴捎?code>.mjs后缀文件名。也就是说,只要脚本文件里面使用import
或者export
命令,那么就必须采用.mjs
后缀名。require
命令不能加载.mjs
文件,会报错,只有import
命令才可以加载.mjs
文件。反过来,.mjs
文件里面也不能使用require
命令,必须使用import
。
目前,这项功能还在试验阶段。安装 Node v8.5.0 或以上版本,要用--experimental-modules
参数才能打开该功能。
目前,Node 的import
命令只支持加载本地模块(file
:协议),不支持加载远程??椤?/p>
- 内部变量
ES6 ??橛Ω檬峭ㄓ玫?,同一个??椴挥眯薷模涂梢杂迷阡榔骰肪澈头衿骰肪?。为了达到这个目标,Node 规定 ES6 ??橹胁荒苁褂?CommonJS 模块的特有的一些内部变量。
首先,就是this关键字。ES6 ??橹校ゲ愕膖his指向undefined
;CommonJS 模块的顶层this指向当前??椋馐橇秸叩囊桓鲋卮蟛钜?。
其次,以下这些顶层变量在 ES6 模块之中都是不存在的。
arguments
require
module
exports
__filename
__dirname
4. 循环加载
“循环加载”(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。