ES6 模块系统

【ES6专栏】全面解析 ECMAScript 6 模块系统
阮一峰:ECMAScript 6 入门

ES6 模块系统

ES6 模块系统它具有以下特性:

  • 使用 export 关键词导出对象。这个关键字可以无限次使用

  • 使用 import 关键字将其它模块导入某一模块中。它可用来导入任意数量的模块

  • 支持模块的异步加载

  • 为加载模块提供编程支持

导出对象

在现有的模块系统中,每个 JavaScript 代码文件在 ES6 中都是一个模块。

只有模块中的对象需要被外部调用时,模块才会输出对象,其余则都是模块的私有对象。
该处理方式将细节进行封装,仅导出必要的功能。

从模块里导出对象,ES6 为我们提供了不同方法,见下面的讨论。

内联导出

ES6 模块里的对象可在创建它们的声明中导出。
一个模块中可无数次使用 export,所有的对象将被一起导出。
请看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export class Employee {
constructor(id, name, dob) {
this.id = id;
this.name = name;
this.dob = dob;
}

getAge() {
return new Date().getFullYear() - this.dob.getYear();
}
}

export function getEmployee(id, name, dob) {
return new Employee(id, name, dob);
}

export const CONFIG = '10.0.0.1';

var emp = new Employee(1, 'Rina', new Date(1987, 1, 22));
// console.log(emp);

/*
* 案例中的模块导出了两个对象: Employee类,getEmployee函数。因对象emp未被导出,所以其仍为模块私有。
*/

导出一组对象

尽管内联导出很有效,但在大规模模块中,它就很难发挥作用了,因为我们可能无法追踪到模块导出来的对象。在这种情况下,更好的办法是,在模块的末尾单独进行导出声明,以导出该模块中的全部对象。

使用单独导出声明重写上一案例中的模块,结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Employee {
constructor(id, name, dob) {
this.id = id;
this.name = name;
this.dob = dob;
}
getAge() {
return new Date().getYear() - this.dob.getYear();
}
}

function getEmployee(id, name, dob) {
return new Employee(id, name, dob);
}

var x = new Employee(1, 'Rina', new Date(1987, 1, 22));

export { Employee, getEmployee };

/**
* 在导出时,重命名对象也是可以的。如下例所示,
* Employee在导出时名字改为了Associate,
* 函数GetEmployee改名为getAssociate。
*/
// export { Employee as Associate, getEmployee as getAssociate };

Default导出

使用关键字default,可将对象标注为default对象导出。default关键字在每一个模块中只能使用一次。它既可以用于内联导出,也可以用于一组对象导出声明中。

下面案例展示了在组导出语句中使用default:

1
2
3
4
export default {  
Employee,
getEmployee
};

导入模块

现有模块可以使用关键字import导入到其它模块。一个模块可以被导入任意数量的模块中。下文展示了导入模块的不同方式。

无对象导入

如果模块包含一些逻辑要执行,且不会导出任何对象,此类对象也可以被导入到另一模块中。如下面案例所示:

1
import './es6_module/doSomethings';

导入默认对象

采用Default导出方式导出对象,该对象在import声明中将直接被分配给某个引用,如下例中的“d”。

1
import d from './module1.js';

导入命名的对象

正如以上讨论的,一个模块可以导出许多命名对象。如果另一模块想导入这些命名对象,需要在导入声明中一一列出这些对象。举个例子:

1
import {Employee, getEmployee} from './module1.js';

当然也可在同一个声明中导入默认对象和命名对象。这种情况下,默认对象必须定义一个别名,如下例。

1
import {default as d, Employee} from './module1.js';

导入所有对象

以上几种情况,只有import声明中列举的对象才会被导入并被使用,而其它对象则无法在导入模块中使用。当然,这就要求用户了解哪些对象可以导出并加以利用。

如果模块导出大量对象,另一模块想引入所有导出的对象,就必须使用如下声明:

1
import * as allFromModule1 from './module1.js';

可编程式的按需导入

如果想基于某些条件或等某个事件发生后再加载需要的模块,可通过使用加载模块的可编程API(programmatic API)来实现。使用 System.import 方法,可按程序设定加载模块。这是一个异步的方法,并返回 Promise

该方法的语法示例如下:

1
2
3
4
5
6
System.import('./module1.js')  
.then(function(module1){
//use module1
}, function(e){
//handle error
});

如果模块加载成功且将导出的模块成功传递给回调函数,Promise 将会通过。如果模块名称有误或由于网络延迟等原因导致模块加载失败,Promise 将会失败。