mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-29 05:42:20 +00:00
docs(module-loader): ES6 循环加载 #542
This commit is contained in:
parent
6d2fc73616
commit
eeba1984a8
@ -505,7 +505,7 @@ var a = require('a');
|
||||
|
||||
### CommonJS 模块的加载原理
|
||||
|
||||
介绍ES6如何处理"循环加载"之前,先介绍目前最流行的CommonJS模块格式的加载原理。
|
||||
介绍 ES6 如何处理“循环加载”之前,先介绍目前最流行的 CommonJS 模块格式的加载原理。
|
||||
|
||||
CommonJS 的一个模块,就是一个脚本文件。`require`命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象。
|
||||
|
||||
@ -610,110 +610,72 @@ ES6 处理“循环加载”与CommonJS有本质的不同。ES6模块是动态
|
||||
请看下面这个例子。
|
||||
|
||||
```javascript
|
||||
// a.js如下
|
||||
import {bar} from './b.js';
|
||||
console.log('a.js');
|
||||
// a.mjs
|
||||
import {bar} from './b';
|
||||
console.log('a.mjs');
|
||||
console.log(bar);
|
||||
export let foo = 'foo';
|
||||
|
||||
// b.js
|
||||
import {foo} from './a.js';
|
||||
console.log('b.js');
|
||||
// b.mjs
|
||||
import {foo} from './a';
|
||||
console.log('b.mjs');
|
||||
console.log(foo);
|
||||
export let bar = 'bar';
|
||||
```
|
||||
|
||||
上面代码中,`a.js`加载`b.js`,`b.js`又加载`a.js`,构成循环加载。执行`a.js`,结果如下。
|
||||
上面代码中,`a.mjs`加载`b.mjs`,`b.mjs`又加载`a.mjs`,构成循环加载。执行`a.mjs`,结果如下。
|
||||
|
||||
```bash
|
||||
$ babel-node a.js
|
||||
b.js
|
||||
undefined
|
||||
a.js
|
||||
bar
|
||||
$ node --experimental-modules a.mjs
|
||||
b.mjs
|
||||
ReferenceError: foo is not defined
|
||||
```
|
||||
|
||||
上面代码中,由于`a.js`的第一行是加载`b.js`,所以先执行的是`b.js`。而`b.js`的第一行又是加载`a.js`,这时由于`a.js`已经开始执行了,所以不会重复执行,而是继续往下执行`b.js`,所以第一行输出的是`b.js`。
|
||||
上面代码中,执行`a.mjs`以后会报错,`foo`变量未定义,这是为什么?
|
||||
|
||||
接着,`b.js`要打印变量`foo`,这时`a.js`还没执行完,取不到`foo`的值,导致打印出来是`undefined`。`b.js`执行完,开始执行`a.js`,这时就一切正常了。
|
||||
让我们一行行来看,ES6 循环加载是怎么处理的。首先,执行`a.mjs`以后,引擎发现它加载了`b.mjs`,因此会优先执行`b.mjs`,然后再执行`a.js`。接着,执行`b.mjs`的时候,已知它从`a.mjs`输入了`foo`接口,这时不会去执行`a.mjs`,而是认为这个接口已经存在了,继续往下执行。执行到第三行`console.log(foo)`的时候,才发现这个接口根本没定义,因此报错。
|
||||
|
||||
再看一个稍微复杂的例子(摘自 Axel Rauschmayer 的[《Exploring ES6》](http://exploringjs.com/es6/ch_modules.html))。
|
||||
解决这个问题的方法,就是让`b.mjs`运行的时候,`foo`已经有定义了。这可以通过将`foo`写成函数来解决。
|
||||
|
||||
```javascript
|
||||
// a.js
|
||||
import {bar} from './b.js';
|
||||
export function foo() {
|
||||
console.log('foo');
|
||||
bar();
|
||||
console.log('执行完毕');
|
||||
}
|
||||
foo();
|
||||
// a.mjs
|
||||
import {bar} from './b';
|
||||
console.log('a.mjs');
|
||||
console.log(bar());
|
||||
function foo() { return 'foo' }
|
||||
export {foo};
|
||||
|
||||
// b.js
|
||||
import {foo} from './a.js';
|
||||
export function bar() {
|
||||
console.log('bar');
|
||||
if (Math.random() > 0.5) {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
// b.mjs
|
||||
import {foo} from './a';
|
||||
console.log('b.mjs');
|
||||
console.log(foo());
|
||||
function bar() { return 'bar' }
|
||||
export {bar};
|
||||
```
|
||||
|
||||
按照 CommonJS 规范,上面的代码是没法执行的。`a`先加载`b`,然后`b`又加载`a`,这时`a`还没有任何执行结果,所以输出结果为`null`,即对于`b.js`来说,变量`foo`的值等于`null`,后面的`foo()`就会报错。
|
||||
|
||||
但是,ES6可以执行上面的代码。
|
||||
这时再执行`a.mjs`就可以得到预期结果。
|
||||
|
||||
```bash
|
||||
$ babel-node a.js
|
||||
$ node --experimental-modules a.mjs
|
||||
b.mjs
|
||||
foo
|
||||
a.mjs
|
||||
bar
|
||||
执行完毕
|
||||
|
||||
// 执行结果也有可能是
|
||||
foo
|
||||
bar
|
||||
foo
|
||||
bar
|
||||
执行完毕
|
||||
执行完毕
|
||||
```
|
||||
|
||||
上面代码中,`a.js`之所以能够执行,原因就在于ES6加载的变量,都是动态引用其所在的模块。只要引用存在,代码就能执行。
|
||||
|
||||
下面,我们详细分析这段代码的运行过程。
|
||||
这是因为函数具有提升作用,在执行`import {bar} from './b'`时,函数`foo`就已经有定义了,所以`b.mjs`加载的时候不会报错。这也意味着,如果把函数`foo`改写成函数表达式,也会报错。
|
||||
|
||||
```javascript
|
||||
// a.js
|
||||
|
||||
// 这一行建立一个引用,
|
||||
// 从`b.js`引用`bar`
|
||||
import {bar} from './b.js';
|
||||
|
||||
export function foo() {
|
||||
// 执行时第一行输出 foo
|
||||
console.log('foo');
|
||||
// 到 b.js 执行 bar
|
||||
bar();
|
||||
console.log('执行完毕');
|
||||
}
|
||||
foo();
|
||||
|
||||
// b.js
|
||||
|
||||
// 建立`a.js`的`foo`引用
|
||||
import {foo} from './a.js';
|
||||
|
||||
export function bar() {
|
||||
// 执行时,第二行输出 bar
|
||||
console.log('bar');
|
||||
// 递归执行 foo,一旦随机数
|
||||
// 小于等于0.5,就停止执行
|
||||
if (Math.random() > 0.5) {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
// a.mjs
|
||||
import {bar} from './b';
|
||||
console.log('a.mjs');
|
||||
console.log(bar());
|
||||
const foo = () => 'foo';
|
||||
export {foo};
|
||||
```
|
||||
|
||||
上面代码的第四行,改成了函数表达式,就不具有提升作用,执行就会报错。
|
||||
|
||||
我们再来看 ES6 模块加载器[SystemJS](https://github.com/ModuleLoader/es6-module-loader/blob/master/docs/circular-references-bindings.md)给出的一个例子。
|
||||
|
||||
```javascript
|
||||
@ -722,13 +684,13 @@ import { odd } from './odd'
|
||||
export var counter = 0;
|
||||
export function even(n) {
|
||||
counter++;
|
||||
return n == 0 || odd(n - 1);
|
||||
return n === 0 || odd(n - 1);
|
||||
}
|
||||
|
||||
// odd.js
|
||||
import { even } from './even';
|
||||
export function odd(n) {
|
||||
return n != 0 && even(n - 1);
|
||||
return n !== 0 && even(n - 1);
|
||||
}
|
||||
```
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user