# 八、模块 模块支持在不污染全局命名空间的情况下导出和导入值。我们已经通过像 AMD 和 CommonJS 这样的第三方库在 JavaScript 中拥有了这种能力,但是 ECMAScript 6 现在也拥有了这种能力。 | ![](img/00003.gif) | 注意:本章处理实际文件,演示 ECMAScript 6 的导出和导入功能。所有“文件”引用都指向与代码片段标题同名的文件,格式如下:code-listing-xxx.js。 | 理解出口的最好方法是在行动中观察它。考虑以下文件: 代码清单 109 ```js export function sum(x, y) {   return x + y; } export var pi = 3.141593; ``` 我们正在导出一个名为`sum`的函数和一个变量`pi`。 让我们进入下一部分,看看如何导入。 现在我们已经创建了自己的模块,让我们利用它并在自己的代码中使用它。考虑以下文件: 代码清单 110 ```js import {sum, pi} from './code-listing-109'; console.log('2 pi = ' + sum(pi, pi)); ``` 在本例中,我们从模块中导入命名导出。 代码清单 111 ```js 2 pi = 6.283186 ``` 如您所见,我们能够访问我们导出的函数和变量。 我们可以为我们指定的出口产品和进口产品提供别名。让我们看另一个例子: 代码清单 112 ```js function sum(x, y) {   return x + y; } var pi = 3.141593; export { sum as add, pi} ``` 在这里,我们同时出口`sum`和`pi`T2,但这次我们选择将`sum`别名为`add`。 让我们看看如何在文件中使用它: 代码清单 113 ```js import {add, pi} from './code-listing-112'; console.log("2 pi = " + add(pi, pi)); ``` 如您所见,我们只是引用了命名的导出,其中包括`sum`的别名。 我们把这个翻过来怎么样?让我们看看下面的文件: 代码清单 114 ```js function sum(x, y) {   return x + y; } var pi = 3.141593; export { sum, pi} ``` 现在让我们导入这个模块,并在文件中提供一个别名: 代码清单 115 ```js import {sum as add, pi} from './code-listing-114'; console.log("2 pi = " + add(pi, pi)); ``` 这给了我们在处理命名出口时很大的灵活性。如果我们知道我们已经有一个使用相同名称的导入,我们可以简单地为我们的导入命名,以避免任何类型的冲突。导入另一个模块后,也可以在自己的模块中导出另一个模块。 请考虑以下语法: 代码清单 116 ```js import {sum as add, pi} from './code-listing-113'; export {sum as add, pi} from './code-listing-113'; console.log("2 pi = " + add(pi, pi)); ``` 它将生产与指定出口完全相同的输出`add`和`pi`。 创作模块时,您可以定义一个`default` `export`。这允许您执行基本的`import`并接收`default`功能。让我们先看看我们需要对文件做些什么: 代码清单 117 ```js export default function sum(x, y) {   return x + y; } function notAvailable() {      console.log("You can't call me..."); } export var pi = 3.141593; ``` 如您所见,这里唯一的区别是我们在第一个函数中添加了`default`关键字。 现在,让我们看看下面的文件: 代码清单 118 ```js import sum from './code-listing-117'; console.log('2 + 3 = ' + sum(2, 3)); ``` 如果你知道你只是在找`default` `export`,这可以是一个方便的功能。如果你正在创作自己的库,并且有一个`default` `export` `,`,这可能会成为一个不错的、一致的特性。因此,即使我们同时导出了`sum`和`pi`,我们所要做的就是命名引用`sum`并相应地使用它。以下是运行上述代码的输出: 代码清单 119 ```js  2 + 3 = 5 ``` | ![](img/00003.gif) | 注意:每个模块只能定义一个默认导出。 | 通配符允许我们加载整个模块,并通过属性符号引用命名的导出。请看下面的例子: 代码清单 120 ```js import * as math from './code-listing-113'; console.log('2 pi = ' + math.sum(math.pi, math.pi)); ``` 当我们执行这段代码时,我们会得到与第一个导入示例相同的结果: 代码清单 121 ```js 2 pi = 6.283186 ``` 你们中的一些人可能会想:当我们使用前面的语法加载整个模块时,有可能访问函数或变量吗?考虑以下代码: 代码清单 122 ```js export function sum(x, y) {   return x + y; } function notAvailable() {      console.log("You can't call me..."); } export var pi = 3.141593; ``` `notAvailable`功能怎么样?让我们看看当我们试图访问它时会发生什么。 代码清单 123 ```js import * as math from './code-listing-122; math.notAvailable(); ``` 这才是让我真正热爱的模块!您有能力决定您希望向外部展示什么,以及您希望在模块内部保留什么。观察当我们尝试运行前面的代码时会发生什么: 代码清单 124 ```js TypeError: math.notAvailable is not a function ``` 这很巧妙。模块加载器识别出您没有将`notAvailable`标识为您希望导出并公开的函数,并且在您尝试运行代码时会抛出一个作用域错误。 本节讨论加载器句柄解析模块说明符(位于`import…from`末尾的字符串标识)、加载模块等。施工方为`Reflect.Loader`。实现 ECMAScript 6 模块的每个浏览器或 transpiler 都在全局变量`System`(系统加载器)中保存一个定制的实例,该变量实现其特定的模块加载风格。 除了我们一直在研究的用于处理模块的声明性语法之外,还有一个编程 API。它允许您执行以下操作: * 以编程方式使用模块和脚本。 * 配置模块加载。 您可以通过基于 ES6 Promises 的应用编程接口以编程方式导入模块,我们将在后面的章节中讨论。现在,请考虑以下几点: 代码清单 125 ```js System.import('some_module')   .then(some_module => {     // Use some_module   })   .catch(error => {      // Handle the error   }); ``` `System.import()`允许您执行以下操作: * 在不支持模块语法的`