mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-24 18:32:22 +00:00
edit module
This commit is contained in:
parent
4473142341
commit
6be5d7e49b
@ -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`时,不会去执行模块,只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
|
||||||
|
@ -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模块的设计思想
|
||||||
|
|
||||||
## 工具
|
## 工具
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user