# 八、模块

模块支持在不污染全局命名空间的情况下导出和导入值。我们已经通过像 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()`允许您执行以下操作:

*   在不支持模块语法的`<script>`标签中使用模块。
*   有条件地加载模块。

`System.import()`检索单个模块，但您可以使用`Promise.all()`导入多个模块:

代码清单 126

```js
  Promise.all(
    ['module1',
  'module2', 'module3']
    .map(x =>
  System.import(x)))
      .then(([module1, module2, module3])
  => {
        //
  Use module1, module2, module3
      });

```

我们将在后面的章节中讨论承诺是如何起作用的。

更多加载器方法:

*   `System.module(source, [options])`–评估模块的源代码中的 JavaScript 代码(通过承诺异步交付)。
*   `System.set(name, module)`–用于注册模块(例如，您通过`System.module()`创建的模块)。
*   `System.define(name, source, [options])` –评估源代码中的模块代码并注册结果。

模块加载器应用编程接口有各种配置钩子。这仍然是一项正在进行的工作。

加载器应用编程接口将允许加载过程的许多定制。例如:

1.  导入时的 Lint 模块，例如通过 JSLint 或 JSHint。
2.  导入时自动翻译模块，即使模块包含 CoffeeScript 或 TypeScript 代码。
3.  使用传统模块，如 AMD 和 Node.js

可配置模块加载是 Node.js 和 CommonJS 受限的领域。