1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 18:32:22 +00:00

edit module

This commit is contained in:
ruanyf 2015-12-06 23:10:32 +08:00
parent 4473142341
commit 6be5d7e49b
2 changed files with 38 additions and 16 deletions

View File

@ -9,18 +9,26 @@ ES6的Class只是面向对象编程的语法糖升级了ES5的构造函数的
ES6模块的设计思想是尽量的静态化使得编译时就能确定模块的依赖关系以及输入和输出的变量。CommonJS和AMD模块都只能在运行时确定这些东西。比如CommonJS模块就是对象输入时必须查找对象属性。 ES6模块的设计思想是尽量的静态化使得编译时就能确定模块的依赖关系以及输入和输出的变量。CommonJS和AMD模块都只能在运行时确定这些东西。比如CommonJS模块就是对象输入时必须查找对象属性。
```javascript ```javascript
// CommonJS模块
let { stat, exists, readFile } = require('fs'); let { stat, exists, readFile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat, exists = _fs.exists, readfile = _fs.readfile;
``` ```
上面代码的实质是整体加载`fs`模块(即加载`fs`的所有方法然后使用时用到3个方法。这种加载称为“运行时加载”。 上面代码的实质是整体加载`fs`模块(即加载`fs`的所有方法),生成一个对象(`_fs`然后再从这个对象上面读取3个方法。这种加载称为“运行时加载”因为只有运行时才能得到这个对象导致完全没办法在编译时做“静态优化”。
ES6模块不是对象而是通过export命令显式指定输出的代码输入时也采用静态命令的形式。 ES6模块不是对象而是通过`export`命令显式指定输出的代码,输入时也采用静态命令的形式。
```javascript ```javascript
// ES6模块
import { stat, exists, readFile } from 'fs'; import { stat, exists, readFile } from 'fs';
``` ```
上面代码的实质是从`fs`模块加载3个方法其他方法不加载。这种加载称为“编译时加载”即ES6可以在编译时就完成模块编译效率要比CommonJS模块的加载方式高。 上面代码的实质是从`fs`模块加载3个方法其他方法不加载。这种加载称为“编译时加载”即ES6可以在编译时就完成模块编译效率要比CommonJS模块的加载方式高。当然这也导致了没法引用ES6模块本身因为它不是对象。
由于ES6模块是编译时加载使得静态分析成为可能。有了它就能进一步拓宽JavaScript的语法比如引入宏macro和类型检验type system这些只能靠静态分析实现的功能。
除了静态加载带来的各种好处ES6模块还有以下好处。 除了静态加载带来的各种好处ES6模块还有以下好处。
@ -105,7 +113,7 @@ export {
上面代码使用`as`关键字,重命名了函数`v1``v2`的对外接口。重命名后,`v2`可以用不同的名字输出两次。 上面代码使用`as`关键字,重命名了函数`v1``v2`的对外接口。重命名后,`v2`可以用不同的名字输出两次。
最后,`export`命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,下面的`import`命令也是如此。 最后,`export`命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,下面的`import`命令也是如此。这是因为处于条件代码块之中就没法做静态优化了违背了ES6模块的设计初衷。
```javascript ```javascript
function foo() { function foo() {
@ -377,9 +385,7 @@ console.log(exp(math.E));
ES6模块加载的机制与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝而ES6模块输出的是值的引用。 ES6模块加载的机制与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝而ES6模块输出的是值的引用。
CommonJS模块输入的是被输出值的拷贝也就是说一旦输出一个值模块内部的变化就影响不到这个值。请看下面这个例子。 CommonJS模块输出的是被输出值的拷贝也就是说一旦输出一个值模块内部的变化就影响不到这个值。请看下面这个模块文件`lib.js`的例子。
下面是一个模块文件`lib.js`
```javascript ```javascript
// lib.js // lib.js
@ -393,9 +399,7 @@ module.exports = {
}; };
``` ```
上面代码输出内部变量`counter`和改写这个变量的内部方法`incCounter` 上面代码输出内部变量`counter`和改写这个变量的内部方法`incCounter`。然后,在`main.js`里面加载这个模块。
然后,加载上面的模块。
```javascript ```javascript
// main.js // main.js
@ -420,14 +424,14 @@ export function incCounter() {
counter++; counter++;
} }
// main1.js // main.js
import { counter, incCounter } from './lib'; import { counter, incCounter } from './lib';
console.log(counter); // 3 console.log(counter); // 3
incCounter(); incCounter();
console.log(counter); // 4 console.log(counter); // 4
``` ```
上面代码说明ES6模块输入的变量`counter`是活的,完全反其所在模块`lib.js`内部的变化。 上面代码说明ES6模块输入的变量`counter`是活的,完全反其所在模块`lib.js`内部的变化。
再举一个出现在`export`一节中的例子。 再举一个出现在`export`一节中的例子。
@ -503,13 +507,13 @@ CommonJS的一个模块就是一个脚本文件。`require`命令第一次加
} }
``` ```
上面代码中该对象的id属性是模块名`exports`属性是模块输出的各个接口,`loaded`属性是一个布尔值,表示该模块的脚本是否执行完毕。其他还有很多属性,这里都省略了。 上面代码中,该对象的`id`属性是模块名,`exports`属性是模块输出的各个接口,`loaded`属性是一个布尔值,表示该模块的脚本是否执行完毕。其他还有很多属性,这里都省略了。
以后需要用到这个模块的时候就会到exports属性上面取值。即使再次执行require命令也不会再次执行该模块而是到缓存之中取值。 以后需要用到这个模块的时候,就会到`exports`属性上面取值。即使再次执行`require`命令,也不会再次执行该模块,而是到缓存之中取值。也就是说CommonJS模块无论加载多少次都只会在第一次加载时运行一次以后再加载就返回第一次运行的结果除非手动清除系统缓存。
### CommonJS模块的循环加载 ### CommonJS模块的循环加载
CommonJS模块的重要特性是加载时执行即脚本代码在`require`的时候,就会全部执行。CommonJS的做法是一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。 CommonJS模块的重要特性是加载时执行即脚本代码在`require`的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。
让我们来看Node[官方文档](https://nodejs.org/api/modules.html#modules_cycles)里面的例子。脚本文件`a.js`代码如下。 让我们来看Node[官方文档](https://nodejs.org/api/modules.html#modules_cycles)里面的例子。脚本文件`a.js`代码如下。
@ -571,6 +575,23 @@ exports.done = true;
总之CommonJS输入的是被输出值的拷贝不是引用。 总之CommonJS输入的是被输出值的拷贝不是引用。
另外由于CommonJS模块遇到循环加载时返回的是当前已经执行的部分的值而不是代码全部执行后的值两者可能会有差异。所以输入变量的时候必须非常小心。
```javascript
var a = require('a'); // 安全的写法
var foo = require('a').foo; // 危险的写法
exports.good = function (arg) {
return a.foo('good', arg); // 使用的是 a.foo 的最新值
};
exports.bad = function (arg) {
return foo('bad', arg); // 使用的是一个部分加载时的值
};
```
上面代码中,如果发生循环加载,`require('a').foo`的值很可能后面会被改写,改用`require('a')`会更保险一点。
### ES6模块的循环加载 ### ES6模块的循环加载
ES6处理“循环加载”与CommonJS有本质的不同。ES6模块是动态引用遇到模块加载命令`import`时,不会去执行模块,只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。 ES6处理“循环加载”与CommonJS有本质的不同。ES6模块是动态引用遇到模块加载命令`import`时,不会去执行模块,只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。

View File

@ -180,6 +180,7 @@
- Axel Rauschmayer, [ECMAScript 6 modules: the final syntax](http://www.2ality.com/2014/09/es6-modules-final.html): ES6模块的介绍以及与CommonJS规格的详细比较 - Axel Rauschmayer, [ECMAScript 6 modules: the final syntax](http://www.2ality.com/2014/09/es6-modules-final.html): ES6模块的介绍以及与CommonJS规格的详细比较
- Dave Herman, [Static module resolution](http://calculist.org/blog/2012/06/29/static-module-resolution/): ES6模块的静态化设计思想 - Dave Herman, [Static module resolution](http://calculist.org/blog/2012/06/29/static-module-resolution/): ES6模块的静态化设计思想
- Jason Orendorff, [ES6 In Depth: Modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/): ES6模块设计思想的介绍 - Jason Orendorff, [ES6 In Depth: Modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/): ES6模块设计思想的介绍
- Ben Newman, [The Importance of import and export](http://benjamn.github.io/empirenode-2015/#/): ES6模块的设计思想
## 工具 ## 工具