在 JavaScript 中,模块只不过是基于函数某些特性的代码组织方式。浏览器
在《你不知道的 JavaScript》中,给出了模块模式因具有的两个必要条件:缓存
从中咱们能够看到一个比较重要的一点,从函数调用所返回的只有数据属性而没有闭包函数的对象并非真正的模块。服务器
你看👀,理解闭包的重要性再次体现出来了。闭包
从以上要求的两点来看,只要知足相应的条件,咱们很容易写出一个模块。异步
const userModule = ((name = 'module') => { let id = 1,moduleName = name; const sayName = () => { console.log('moduleName: %s', moduleName); }; const sayId = () => { console.log('id: %s', id); }; const changeName = value => { moduleName = value; }; const changePublicAPI = () => { publicAPI.sayIdentification = sayId }; const publicAPI = { sayIdentification: sayName, changeName, changePublicAPI, } return publicAPI; })();
以上在知足两个必要的基础上转换成了 IIFE(当即执行函数表达式)。同时能够看出,基于函数的模块能够在运行时经过内部保留着公共 API 对象的引用,从而对模块实例进行修改。模块化
模块的出现也是为了可以提升代码的复用率,方便代码管理。复用模块,天然会出现模块依赖的问题,因此说咱们须要一个管理模块依赖的模块。函数
const moduleManage = (() => { let modules = {}; const define = (name, deps, module) => { deps = deps.map(item => modules[item]) modules[name] = module(...deps); }; const exports = (name) => { return modules[name]; } return { define, exports, } })(); moduleManage.define('a', [], () => { const sayName = name => { console.log('name: %s', name); }; return { sayName, } }); moduleManage.define('b', ['a'], (a) => { let name = 'b'; const sayName = () => { a.sayName(name) }; return { sayName, } }); var b = moduleManage.exports('b'); b.sayName();
模块依赖管理器也依然是个模块,这里的实现其实很简单。modules[name] = module(...deps)
,使用 modules 缓存各个模块,对于依赖模块的模块,则把依赖做为参数使用。ui
CommonJS 规范服务于服务端,同步阻塞,在写法风格上是依赖就近。可是在浏览器上,CommonJS 就很差使了,浏览器须要从服务器请求数据,下载完成后才会有下一步的执行。若是采用 CommonJS 的同步方式,指不定何时文件才会下载完成。code
为了推广到浏览器上,AMD 规范采用异步方式加载模块。先异步加载模块,加载完成后就能够在回调中使用依赖模块了。这样就保证了在使用依赖时,依赖已经加载完成。AMD 规范是早早地下载,早早地执行,在回调里 require
的是依赖的引用。在写法风格上是依赖前置,这种风格已经不一样于 CommonJS 了。还有,这里早早地执行会带来一个问题,若是存在某个依赖某些条件不成立,致使没有用上。那么,这里的早早地执行岂不是画蛇添足了?对象
CMD 规范是 sea.js 推崇的规范,它采用的也是异步加载模块的方式,只是在依赖模块的执行时机上有所不一样。在写法风格上,又回归到 CommonJS,依赖就近。sea.js 是早早地下载,延迟执行。
到了 ES6,终于从语法上支持模块化了,ES6 模块是编译时加载,使得在编译时就能肯定模块的依赖关系,并且在未来服务器和浏览器都会支持 ES6 的模块化方案。