08.md 7.3 KB
Newer Older
W
wizardforcel 已提交
1 2
# 八、模块

W
wizardforcel 已提交
3 4
模块支持在不污染全局命名空间的情况下导出和导入值。我们已经通过像 AMD 和 CommonJS 这样的第三方库在 JavaScript 中拥有了这种能力,但是 ECMAScript 6 现在也拥有了这种能力。

W
wizardforcel 已提交
5
| ![](img/00003.gif) | 注意:本章处理实际文件,演示 ECMAScript 6 的导出和导入功能。所有“文件”引用都指向与代码片段标题同名的文件,格式如下:code-listing-xxx.js。 |
W
wizardforcel 已提交
6 7 8 9 10

理解出口的最好方法是在行动中观察它。考虑以下文件:

代码清单 109

W
wizardforcel 已提交
11
```js
W
wizardforcel 已提交
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
  export function sum(x, y) {
    return
  x + y;
  }
  export var pi = 3.141593; 

```

我们正在导出一个名为`sum`的函数和一个变量`pi`

让我们进入下一部分,看看如何导入。

现在我们已经创建了自己的模块,让我们利用它并在自己的代码中使用它。考虑以下文件:

代码清单 110

W
wizardforcel 已提交
28
```js
W
wizardforcel 已提交
29 30 31 32 33 34 35 36 37 38 39 40
  import {sum, pi}
  from './code-listing-109';

  console.log('2
  pi = ' + sum(pi, pi));

```

在本例中,我们从模块中导入命名导出。

代码清单 111

W
wizardforcel 已提交
41
```js
W
wizardforcel 已提交
42 43 44 45 46 47 48 49 50 51
  2 pi = 6.283186 

```

如您所见,我们能够访问我们导出的函数和变量。

我们可以为我们指定的出口产品和进口产品提供别名。让我们看另一个例子:

代码清单 112

W
wizardforcel 已提交
52
```js
W
wizardforcel 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
  function sum(x, y) {
    return
  x + y;
  }
  var pi = 3.141593; 

  export { sum as add,
  pi}

```

在这里,我们同时出口`sum``pi`T2,但这次我们选择将`sum`别名为`add`

让我们看看如何在文件中使用它:

代码清单 113

W
wizardforcel 已提交
70
```js
W
wizardforcel 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
  import {add, pi}
  from './code-listing-112';

  console.log("2
  pi = " + add(pi,
  pi));

```

如您所见,我们只是引用了命名的导出,其中包括`sum`的别名。

我们把这个翻过来怎么样?让我们看看下面的文件:

代码清单 114

W
wizardforcel 已提交
86
```js
W
wizardforcel 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100
  function sum(x, y) {
    return
  x + y;
  }
  var pi = 3.141593; 

  export { sum, pi}

```

现在让我们导入这个模块,并在文件中提供一个别名:

代码清单 115

W
wizardforcel 已提交
101
```js
W
wizardforcel 已提交
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
  import {sum as add,
  pi} from './code-listing-114';

  console.log("2
  pi = " + add(pi,
  pi));

```

这给了我们在处理命名出口时很大的灵活性。如果我们知道我们已经有一个使用相同名称的导入,我们可以简单地为我们的导入命名,以避免任何类型的冲突。导入另一个模块后,也可以在自己的模块中导出另一个模块。

请考虑以下语法:

代码清单 116

W
wizardforcel 已提交
117
```js
W
wizardforcel 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
  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

W
wizardforcel 已提交
135
```js
W
wizardforcel 已提交
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
  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

W
wizardforcel 已提交
155
```js
W
wizardforcel 已提交
156 157 158 159 160 161 162 163 164 165 166
  import sum from './code-listing-117';

  console.log('2
  + 3 = ' + sum(2, 3));

```

如果你知道你只是在找`default` `export`,这可以是一个方便的功能。如果你正在创作自己的库,并且有一个`default` `export` `,`,这可能会成为一个不错的、一致的特性。因此,即使我们同时导出了`sum``pi`,我们所要做的就是命名引用`sum`并相应地使用它。以下是运行上述代码的输出:

代码清单 119

W
wizardforcel 已提交
167
```js
W
wizardforcel 已提交
168 169 170 171
   2 + 3 = 5 

```

W
wizardforcel 已提交
172
| ![](img/00003.gif) | 注意:每个模块只能定义一个默认导出。 |
W
wizardforcel 已提交
173 174 175 176 177

通配符允许我们加载整个模块,并通过属性符号引用命名的导出。请看下面的例子:

代码清单 120

W
wizardforcel 已提交
178
```js
W
wizardforcel 已提交
179 180 181 182 183 184 185 186 187 188 189 190
  import * as math from './code-listing-113';

  console.log('2
  pi = ' + math.sum(math.pi,
  math.pi));

```

当我们执行这段代码时,我们会得到与第一个导入示例相同的结果:

代码清单 121

W
wizardforcel 已提交
191
```js
W
wizardforcel 已提交
192 193 194 195 196 197 198 199
  2 pi = 6.283186 

```

你们中的一些人可能会想:当我们使用前面的语法加载整个模块时,有可能访问函数或变量吗?考虑以下代码:

代码清单 122

W
wizardforcel 已提交
200
```js
W
wizardforcel 已提交
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
  export function sum(x, y) {
    return
  x + y;
  }
  function
  notAvailable() {
       console.log("You can't call me...");
  }
  export var pi = 3.141593;

```

`notAvailable`功能怎么样?让我们看看当我们试图访问它时会发生什么。

代码清单 123

W
wizardforcel 已提交
217
```js
W
wizardforcel 已提交
218 219 220 221 222 223 224 225 226 227
  import * as math from './code-listing-122;

  math.notAvailable();

```

这才是让我真正热爱的模块!您有能力决定您希望向外部展示什么,以及您希望在模块内部保留什么。观察当我们尝试运行前面的代码时会发生什么:

代码清单 124

W
wizardforcel 已提交
228
```js
W
wizardforcel 已提交
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
  TypeError:
  math.notAvailable is not a function 

```

这很巧妙。模块加载器识别出您没有将`notAvailable`标识为您希望导出并公开的函数,并且在您尝试运行代码时会抛出一个作用域错误。

本节讨论加载器句柄解析模块说明符(位于`import…from`末尾的字符串标识)、加载模块等。施工方为`Reflect.Loader`。实现 ECMAScript 6 模块的每个浏览器或 transpiler 都在全局变量`System`(系统加载器)中保存一个定制的实例,该变量实现其特定的模块加载风格。

除了我们一直在研究的用于处理模块的声明性语法之外,还有一个编程 API。它允许您执行以下操作:

*   以编程方式使用模块和脚本。
*   配置模块加载。

您可以通过基于 ES6 Promises 的应用编程接口以编程方式导入模块,我们将在后面的章节中讨论。现在,请考虑以下几点:

代码清单 125

W
wizardforcel 已提交
247
```js
W
wizardforcel 已提交
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
  System.import('some_module')
    .then(some_module => {
      //
  Use some_module
    })
    .catch(error
  => {
       //
  Handle the error
    });

```

`System.import()`允许您执行以下操作:

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

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

代码清单 126

W
wizardforcel 已提交
270
```js
W
wizardforcel 已提交
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
  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 受限的领域。