一. 模块化的功能:
1. 代码分离,利于维护。
2. 解决全局变量重命名的问题。
3. 提供代码复用性和组合性。
二. 分类: ES6 Module,CommonJs,AMD,CMD。
三. ES6 Module(export/import)
1. 编译时确定模块的依赖关系。
2. es6使用import时,不会去执行模块,而是生成模块的一个引用。等使用的时候,才会去相应的模块中取值。因为是动态引用所以不存在缓存问题。export和import具有提升性。
3. 输出的是一个值得引用。
4. 模块顶层的this不再指向当前模块,而是指向undefined。
5. 处理循环加载(模块a引用了模块b,模块b引用了模块a)的问题,和commonjs的处理方式不同:
// a.mjsimport {bar} from './b';console.log('a.mjs');console.log(bar());var foo = 'a.js'export {foo};// b.mjsimport {foo} from './a';console.log('b.mjs');console.log(foo);function bar() { return 'bar' }export {bar};复制代码
解析:首先执行模块a,发现a中引用了模块b,此时执行权交给模块b,模块b执行,发现引用了模块a,此时不去执行模块a,而会认为这个模块a导出的接口是存在的(实际上a模块的导出还没有执行,因为a模块中引用b模块在第一行,暂停执行了),继续执行。当下面代码有访问到a模块导出的接口时,因为export/import具有声明提升性(而且var声明有声明提升),所以此时a.js相当于导出了{ foo: undefined }, 所以会打印foo为undefined。然后继续执行完b后再执行a.js,此时访问b.js的bar函数,因为b.js中是函数表达式声明的bar函数,所以会声明提升,所以此时b.js相当于{ bar: function(){ return 'bar' } },所以在a.js中打印了‘bar’;
四. CommonJs(module.exports/require)
1. 运行时确定模块的依赖。
2. 同步加载。
3. 模块加载的顺序就是代码中编写的顺序。
4. require命令在第一次执行的时候会去加载并且执行脚本,然后在内存中生成此脚本返回的exports对象,除非手动清除缓存,不然以后都会从缓存中取值。缓存是根据模块的绝对路径来识别的,同一个模块在不同路径下,还是会重新加载此模块。
5. 若出现循环加载的问题,只会加载当前模块已经加载的部分,例如:
exports.done = false;
var b = require('./b.js');console.log('在 a.js 之中,b.done = %j', b.done);exports.done = true;console.log('a.js 执行完毕');========================================================exports.done = false;var a = require('./a.js');console.log('在 b.js 之中,a.done = %j', a.done);exports.done = true;console.log('b.js 执行完毕');解析:a模块首先输出一个done = false;然后输入b.js,此时,a.js停止执行,等待b.js执行完毕,再继续执行;然后进入b.js,b.js执行到第二行会去加载a.js,此时加载回来的a.js只有a.js中已经执行了的done = false;然后b.js继续执行,b.js执行完毕了,吧执行权交给a.js.复制代码
6. 输出的是一个值得拷贝。
7. 模块顶层的this指向当前模块
五. AMD(异步加载模块)
1. 依赖前置。
2. 适合浏览器端,异步加载模块,避免阻塞网页渲染。
3. 代表: requirejs。
六. CMD
1. 依赖就近。
2. 需要用到依赖的时候才回去声明。
3.代表: seajs。