Skip to content

Commit bf75bab

Browse files
committed
edit module
1 parent 6c6eb3a commit bf75bab

File tree

1 file changed

+109
-48
lines changed

1 file changed

+109
-48
lines changed

docs/module.md

Lines changed: 109 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ import { stat, exists, readFile } from 'fs';
2222

2323
上面代码的实质是从`fs`模块加载3个方法,其他方法不加载。这种加载称为“编译时加载”,即ES6可以在编译时就完成模块编译,效率要比CommonJS模块的加载方式高。
2424

25+
除了静态加载带来的各种好处,ES6模块还有以下好处。
26+
27+
- 不再需要UMD模块格式了,将来服务器和浏览器都会支持ES6模块格式。目前,通过各种工具库,其实已经做到了这一点。
28+
- 将来浏览器的新API就能用模块格式提供,不再必要做成全局变量或者`navigator`对象的属性。
29+
- 不再需要对象作为命名空间(比如`Math`对象),未来这些功能可以通过模块提供。
30+
2531
## 严格模式
2632

2733
ES6的模块自动采用严格模式,不管你有没有在模块头部加上`"use strict"`
@@ -141,22 +147,6 @@ function setName(element) {
141147
import { lastName as surname } from './profile';
142148
```
143149

144-
ES6支持多重加载,即所加载的模块中又加载其他模块。
145-
146-
```javascript
147-
import { Vehicle } from './Vehicle';
148-
149-
class Car extends Vehicle {
150-
move () {
151-
console.log(this.name + ' is spinning wheels...')
152-
}
153-
}
154-
155-
export { Car }
156-
```
157-
158-
上面的模块先加载`Vehicle`模块,然后在其基础上添加了`move`方法,再作为一个新模块输出。
159-
160150
注意,`import`命令具有提升效果,会提升到整个模块的头部,首先执行。
161151

162152
```javascript
@@ -356,19 +346,19 @@ let o = new MyClass();
356346

357347
模块之间也可以继承。
358348

359-
假设有一个circleplus模块,继承了circle模块
349+
假设有一个`circleplus`块,继承了`circle`模块
360350

361351
```javascript
362352
// circleplus.js
363353

364354
export * from 'circle';
365355
export var e = 2.71828182846;
366356
export default function(x) {
367-
return Math.exp(x);
357+
return Math.exp(x);
368358
}
369359
```
370360

371-
上面代码中的`export *`表示输出`circle`模块的所有属性和方法,`export default`命令定义模块的默认方法
361+
上面代码中的`export *`表示再输出`circle`模块的所有属性和方法。注意`export *`命令会忽略`circle`模块的`default`方法。然后,上面代码又输出了自定义的`e`变量和默认方法
372362

373363
这时,也可以将`circle`的属性或方法,改名后再输出。
374364

@@ -392,6 +382,103 @@ console.log(exp(math.E));
392382

393383
上面代码中的`import exp`表示,将`circleplus`模块的默认方法加载为`exp`方法。
394384

385+
## ES6模块加载的实质
386+
387+
ES6模块加载的机制,与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。
388+
389+
CommonJS模块输入的是被输出值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。请看下面这个例子。
390+
391+
下面是一个模块文件`lib.js`
392+
393+
```javascript
394+
// lib.js
395+
var counter = 3;
396+
function incCounter() {
397+
counter++;
398+
}
399+
module.exports = {
400+
counter: counter,
401+
incCounter: incCounter,
402+
};
403+
```
404+
405+
上面代码输出内部变量`counter`和改写这个变量的内部方法`incCounter`
406+
407+
然后,加载上面的模块。
408+
409+
```javascript
410+
// main.js
411+
var counter = require('./lib').counter;
412+
var incCounter = require('./lib').incCounter;
413+
414+
console.log(counter); // 3
415+
incCounter();
416+
console.log(counter); // 3
417+
```
418+
419+
上面代码说明,`counter`输出以后,`lib.js`模块内部的变化就影响不到`counter`了。
420+
421+
ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令`import`时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值,换句话说,ES6的输入有点像Unix系统的”符号连接“,原始值变了,输入值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
422+
423+
还是举上面的例子。
424+
425+
```javascript
426+
// lib.js
427+
export let counter = 3;
428+
export function incCounter() {
429+
counter++;
430+
}
431+
432+
// main1.js
433+
import { counter, incCounter } from './lib';
434+
console.log(counter); // 3
435+
incCounter();
436+
console.log(counter); // 4
437+
```
438+
439+
上面代码说明,ES6模块输入的变量`counter`是活的,完全反映其所在模块`lib.js`内部的变化。
440+
441+
还是举本章开头时的例子。
442+
443+
```javascript
444+
// m1.js
445+
export var foo = 'bar';
446+
setTimeout(() => foo = 'baz', 500);
447+
448+
// m2.js
449+
import {foo} from './m1.js';
450+
console.log(foo);
451+
setTimeout(() => console.log(foo), 500);
452+
```
453+
454+
上面代码中,`m1.js`的变量`foo`,在刚加载时等于`bar`,过了500毫秒,又变为等于`baz`
455+
456+
让我们看看,`m2.js`能否正确读取这个变化。
457+
458+
```bash
459+
$ babel-node m2.js
460+
461+
bar
462+
baz
463+
```
464+
465+
上面代码表明,ES6模块不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。
466+
467+
由于ES6输入的模块变量,只是一个”符号连接“,所以这个变量是只读的,对它进行重新赋值会报错。
468+
469+
```javascript
470+
// lib.js
471+
export let obj = {};
472+
473+
// main.js
474+
import { obj } from './lib';
475+
476+
obj.prop = 123; // OK
477+
obj = {}; // TypeError
478+
```
479+
480+
上面代码中,`main.js``lib.js`输入变量`obj`,可以对`obj`添加属性,但是重新赋值就会报错。因为变量`obj`指向的地址是只读的,不能重新赋值,这就好比`main.js`创造了一个名为`obj`的const变量。
481+
395482
## 循环加载
396483

397484
“循环加载”(circular dependency)指的是,`a`脚本的执行依赖`b`脚本,而`b`脚本的执行又依赖`a`脚本。
@@ -472,37 +559,11 @@ a.js 执行完毕
472559
exports.done = true;
473560
```
474561

475-
### ES6模块
476-
477-
ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令`import`时,不会去执行模块,而是只生成一个引用。等到真的需要用到时,再到模块里面去取值。
478-
479-
因此,ES6模块是动态引用,不存在缓存值的问题,而且模块里面的变量,绑定其所在的模块。还是举本章开头时的例子。
562+
总之,CommonJS输入的是被输出值的拷贝。
480563

481-
```javascript
482-
// m1.js
483-
export var foo = 'bar';
484-
setTimeout(() => foo = 'baz', 500);
485-
486-
// m2.js
487-
import {foo} from './m1.js';
488-
console.log(foo);
489-
setTimeout(() => console.log(foo), 500);
490-
```
491-
492-
上面代码中,`m1.js`的变量`foo`,在刚加载时等于`bar`,过了500毫秒,又变为等于`baz`
493-
494-
让我们看看,`m2.js`能否正确读取这个变化。
495-
496-
```bash
497-
$ babel-node m2.js
498-
499-
bar
500-
baz
501-
```
502-
503-
上面代码表明,ES6模块不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。
564+
### ES6模块
504565

505-
这导致ES6处理“循环加载”与CommonJS有本质的不同。ES6根本不会检查是否发生了“循环加载”,只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
566+
ES6处理“循环加载”与CommonJS有本质的不同。ES6模块是动态引用,根本不会检查是否发生了“循环加载”,只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
506567

507568
请看下面的例子(摘自 Dr. Axel Rauschmayer 的[《Exploring ES6》](http://exploringjs.com/es6/ch_modules.html))。
508569

0 commit comments

Comments
 (0)