@@ -22,6 +22,12 @@ import { stat, exists, readFile } from 'fs';
22
22
23
23
上面代码的实质是从` fs ` 模块加载3个方法,其他方法不加载。这种加载称为“编译时加载”,即ES6可以在编译时就完成模块编译,效率要比CommonJS模块的加载方式高。
24
24
25
+ 除了静态加载带来的各种好处,ES6模块还有以下好处。
26
+
27
+ - 不再需要UMD模块格式了,将来服务器和浏览器都会支持ES6模块格式。目前,通过各种工具库,其实已经做到了这一点。
28
+ - 将来浏览器的新API就能用模块格式提供,不再必要做成全局变量或者` navigator ` 对象的属性。
29
+ - 不再需要对象作为命名空间(比如` Math ` 对象),未来这些功能可以通过模块提供。
30
+
25
31
## 严格模式
26
32
27
33
ES6的模块自动采用严格模式,不管你有没有在模块头部加上` "use strict" ` 。
@@ -141,22 +147,6 @@ function setName(element) {
141
147
import { lastName as surname } from ' ./profile' ;
142
148
```
143
149
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
-
160
150
注意,` import ` 命令具有提升效果,会提升到整个模块的头部,首先执行。
161
151
162
152
``` javascript
@@ -356,19 +346,19 @@ let o = new MyClass();
356
346
357
347
模块之间也可以继承。
358
348
359
- 假设有一个circleplus模块,继承了circle模块 。
349
+ 假设有一个 ` circleplus ` 块,继承了 ` circle ` 模块 。
360
350
361
351
``` javascript
362
352
// circleplus.js
363
353
364
354
export * from ' circle' ;
365
355
export var e = 2.71828182846 ;
366
356
export default function (x ) {
367
- return Math .exp (x);
357
+ return Math .exp (x);
368
358
}
369
359
```
370
360
371
- 上面代码中的` export * ` ,表示输出 ` circle ` 模块的所有属性和方法,` export default ` 命令定义模块的默认方法 。
361
+ 上面代码中的` export * ` ,表示再输出 ` circle ` 模块的所有属性和方法。注意 ,` export * ` 命令会忽略 ` circle ` 模块的 ` default ` 方法。然后,上面代码又输出了自定义的 ` e ` 变量和默认方法 。
372
362
373
363
这时,也可以将` circle ` 的属性或方法,改名后再输出。
374
364
@@ -392,6 +382,103 @@ console.log(exp(math.E));
392
382
393
383
上面代码中的` import exp ` 表示,将` circleplus ` 模块的默认方法加载为` exp ` 方法。
394
384
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
+
395
482
## 循环加载
396
483
397
484
“循环加载”(circular dependency)指的是,` a ` 脚本的执行依赖` b ` 脚本,而` b ` 脚本的执行又依赖` a ` 脚本。
@@ -472,37 +559,11 @@ a.js 执行完毕
472
559
exports .done = true ;
473
560
```
474
561
475
- ### ES6模块
476
-
477
- ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令` import ` 时,不会去执行模块,而是只生成一个引用。等到真的需要用到时,再到模块里面去取值。
478
-
479
- 因此,ES6模块是动态引用,不存在缓存值的问题,而且模块里面的变量,绑定其所在的模块。还是举本章开头时的例子。
562
+ 总之,CommonJS输入的是被输出值的拷贝。
480
563
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模块
504
565
505
- 这导致ES6处理 “循环加载”与CommonJS有本质的不同。ES6根本不会检查是否发生了 “循环加载”,只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
566
+ ES6处理 “循环加载”与CommonJS有本质的不同。ES6模块是动态引用,根本不会检查是否发生了 “循环加载”,只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
506
567
507
568
请看下面的例子(摘自 Dr. Axel Rauschmayer 的[ 《Exploring ES6》] ( http://exploringjs.com/es6/ch_modules.html ) )。
508
569
0 commit comments