@@ -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
2733ES6的模块自动采用严格模式,不管你有没有在模块头部加上` "use strict" ` 。
@@ -141,22 +147,6 @@ function setName(element) {
141147import { 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
364354export * from ' circle' ;
365355export var e = 2.71828182846 ;
366356export 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 执行完毕
472559exports .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