chenqunfeng / Blog

个人技术记录博客
6 stars 1 forks source link

JavaScript的模块循环加载 #4

Closed chenqunfeng closed 7 years ago

chenqunfeng commented 7 years ago

循环加载

循环加载,就是说a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本,而这样的结果便会导致循环加载。 为了应对这种循环加载导致的递归加载,使程序无法进行,我们需要有一些方法来避免这个问题。

CommonJS

CommonJS模块的加载原理

当require命令第一次加载模块时,就会执行整个模块,然后在内存中生成一个对象。

{
  id: '...'
  exports: {}
  loaded: true
  ...
}

其中id是模块名,exports是模块输出的接口,loaded表示模块是否执行完毕。 而当再次执行require命令,不会重复执行模块,而是从缓存中取值。

CommonJS模块循环加载

CommonJS的处理方式:一旦出现某个模块被“循环加载”,就只输出已经执行的部分,还未执行的部分不输出。

官方文档说明中的例子分析。

a.js

console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

b.js

console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');

main.js

console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);

1、main.js开始执行

第一行:控制台输出'main starting'
第二行:执行a.js

2、执行a.js,

第一行:控制台输出'a starting'
第二行:a模块done属性为false
第三行:执行b.js

3、执行b.js

第一行:控制台输出‘b starting’
第二行:b模块done属性为false
第三行:a.js已经执行过,所以不会重复执行
第四行:控制台输出'in b, a.done = false'
第五行:b模块的done属性变更为true
第六行:控制台输出'b done'

4、b.js执行完毕,回到a.js中

第四行:控制台输出'in a, b.done = true'
第五行:a模块的done属性变更为true
第六行:最后输出'a done'

5、a.js执行完毕,回到main.js中

第三行:b.js文件已经执行过,所以不会重复执行
第四行:控制台输出'in main, a.done=true, b.done=true'

ES6

ES6模块的运行机制与CommonJS不同,它遇到模块加载命令import时,不会执行模块,而是只生成一个引用。从这个角度上看,ES6不关心是否发生了循环引用,它只负责生成一个引用,需要开发者自己保证,真正调用的时候不会出错即可。