ES6模块是静态化的设计思想,使得编译时就能确定模块之间的依赖关系,以及输入和输出的变量。
ES6模块自动采用严格模式,即使没有在模块的头部添加use strict
在模块当中,this受到了限制,顶层的this指向undefined,即不应该在顶层代码中使用this
模块功能主要由exportimport两个命令实现,export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
exportimport命令可以出现在模块的任何位置,但要处于模块的顶层。如果处于块级作用域内,则会报错,即无法实现模块的按需加载及动态加载。
一个模块就是一个独立的文件。该文件内部的所有变量,外部都无法获取,除非使用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 文件会被认定为模块。