Closed chenqunfeng closed 7 years ago
循环加载,就是说a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本,而这样的结果便会导致循环加载。 为了应对这种循环加载导致的递归加载,使程序无法进行,我们需要有一些方法来避免这个问题。
当require命令第一次加载模块时,就会执行整个模块,然后在内存中生成一个对象。
{ id: '...' exports: {} loaded: true ... }
其中id是模块名,exports是模块输出的接口,loaded表示模块是否执行完毕。 而当再次执行require命令,不会重复执行模块,而是从缓存中取值。
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模块的运行机制与CommonJS不同,它遇到模块加载命令import时,不会执行模块,而是只生成一个引用。从这个角度上看,ES6不关心是否发生了循环引用,它只负责生成一个引用,需要开发者自己保证,真正调用的时候不会出错即可。
循环加载
循环加载,就是说a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本,而这样的结果便会导致循环加载。 为了应对这种循环加载导致的递归加载,使程序无法进行,我们需要有一些方法来避免这个问题。
CommonJS
CommonJS模块的加载原理
当require命令第一次加载模块时,就会执行整个模块,然后在内存中生成一个对象。
其中id是模块名,exports是模块输出的接口,loaded表示模块是否执行完毕。 而当再次执行require命令,不会重复执行模块,而是从缓存中取值。
CommonJS模块循环加载
CommonJS的处理方式:一旦出现某个模块被“循环加载”,就只输出已经执行的部分,还未执行的部分不输出。
官方文档说明中的例子分析。
a.js
b.js
main.js
1、main.js开始执行
2、执行a.js,
3、执行b.js
4、b.js执行完毕,回到a.js中
5、a.js执行完毕,回到main.js中
ES6
ES6模块的运行机制与CommonJS不同,它遇到模块加载命令import时,不会执行模块,而是只生成一个引用。从这个角度上看,ES6不关心是否发生了循环引用,它只负责生成一个引用,需要开发者自己保证,真正调用的时候不会出错即可。