1.模块化定义
将一个复杂程序依据一定的规则(规范)封装为几个块(文件),并组合到一起。各个块内部数据和实现都是私有的,通过对外暴露调用接口与其他部分进行整合,每个块之间相互独立,但又可以互相调用和通信。
2.jQuery
(function(window,undefined){var jQuery = ...window.jQuery = window.$ = jQuery})(window)
ES6之前,常用的解决方案为IIFE模式,例如jQuery,将数据和行为封装至jQuery变量中,最后通过将变量挂载至window对象上来对外暴露接口
缺点:
- 污染全局作用域
- 模块之间的依赖关系模糊
- 模块加载顺序无法保证
3.CommonJS
NodeJS是CommonJS的主要实践者,每个文件就是一个模块。CommonJS采用同步的方式加载模块,在服务器端因为本地文件的IO操作很快,所以采取同步的方式并无太大问题, 但对于浏览器端,文件均是通过网络加载的,单线程阻塞在模块加载上显然是不可行的。
主要特点:
- 所有代码均运行在模块作用域,不会污染全局作用域
- 模块可以多次加载,第一次加载后便会缓存,此后加载便会读取缓存,想要模块再次运行,则需要清除缓存
- 模块加载的顺序由其在代码中出现的顺序决定
加载机制:
CommonJS模块输出的是值的拷贝,一旦输出后,模块内部的变化就不会影响到这个值。与ES6module存在较大差异。
CommonJS的主要特征为使用require导入依赖,使用module.exports导出接口
AMD
相比于CommonJS,AMD异步加载模块,允许指定回调函数,遵循AMD规范的主要例子为require.js。
require.js的基本思想为define定义模块,require加载模块。
定义模块时可以指定其回调函数define(deps, callback)
ESM
ES6时,制定了JS模块标准ES Modules ,使用import声明依赖,使用export导出接口
ESM的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。由此可知import和export语句是不应该出现在块级作用域的,例如if语句中进行依赖声明,这违背了ESM静态化的思想。静态化的主要优势便是可以通过静态分析得出导入的依赖,若是依赖没有使用,便可以通过 tree shaking等手段去除,从而减少代码体积提升运行性能。
特点:
- 静态化
- 依赖引入为只读,无法进行修改
- 无缓存,均为动态引用
加载机制:
ESM加载的是值的引用。JS引擎对脚本进行静态分析时,遇到import指令便会生成一个只读引用,等到脚本真正执行的时候,再依据这个引用到对应模块中取值。
