ES6模块是静态化的设计思想,使得编译时就能确定模块之间的依赖关系,以及输入和输出的变量。
ES6模块自动采用严格模式,即使没有在模块的头部添加use strict
。
在模块当中,this
受到了限制,顶层的this
指向undefined
,即不应该在顶层代码中使用this
。
模块功能主要由export
和import
两个命令实现,export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。export
和import
命令可以出现在模块的任何位置,但要处于模块的顶层。如果处于块级作用域内,则会报错,即无法实现模块的按需加载及动态加载。
一个模块就是一个独立的文件。该文件内部的所有变量,外部都无法获取,除非使用export
向外输出,输出的内容可以是变量、函数、类等。
export
// test.js
//输出变量
var userName = 'Neo';
//输出函数
function sayHi() {
console.log('Hi from module.');
}
//以接口形式输出
export {userName, sayHi};
import
使用import
命令加载其他模块提供的功能,即可以指定加载某个输出值,也可以使用*
指定一个对象进行整体加载。
// test.html
<script type="module">
//导入模块
import {userName, sayHi} from './test.js';
//引用模块中的变量和方法
console.log('Hi, ', userName);
sayHi();
</script>
此处需要特别注意,如果是在浏览器中进行调试的话,需要显式设置<script>
标签的type
类型为module
,否则会报错:Uncaught SyntaxError: Unexpected token
。
模块是作为一整块 JavaScript 代码而存在的,带有 type="module"
属性的<script>
标签会告诉浏览器相关代码应该作为模块执行,而不是作为传统的脚本执行。模块可以嵌入在网页中,也可以作为外部文件引入:
<script type="module">
// 模块代码
</script>
<script type="module" src="path/to/myModule.js"></script>
export default
使用export default
命令,为模块指定默认输出。其他模块在加载该模块时,就可以指定任意名称来输入,而无需预先知道要加载的变量或函数名称。
一个模块只能有一个默认输出,因此在导入默认输出时,import
命令后不需要加大括号,否则会报错:The requested module './xxx.js' does not provide an export named 'xxx'
。
// test.js
//默认输出
export default {
userName: 'Neo',
sayHi: function() {
console.log("Hi from module.");
}
}
// test.html
<script type="module">
//导入模块
import foo from './test.js';
//引用模块中的变量和方法
console.log('Hi, ', foo.userName);
foo.sayHi();
</script>
模块行为
ES6模块借用了CommonJS和AMD的很多优秀特性,包括:
- 模块代码只在加载后执行
- 模块只能加载一次
- 模块是单例
- 模块可以定义公共接口,其他模块可以基于这个公共接口观察和交互
- 模块可以请求加载其他模块
- 支持循环依赖
ES6模块系统也增加了一些新的行为:
- ES6 模块默认在严格模式下执行
- ES6 模块不共享全局命名空间
- 模块顶级 this 的值是 undefined(常规脚本中是 window)
- 模块中的 var 声明不会添加到 window 对象
- ES6 模块是异步加载和执行的
浏览器运行时在知道应该把某个文件当成模块时,会有条件地按照上述 ES6 模块行为来施加限制。与<script type="module">
关联或者通过 import 语句加载的 JavaScript 文件会被认定为模块。