1.模块化定义

将一个复杂程序依据一定的规则(规范)封装为几个块(文件),并组合到一起。各个块内部数据和实现都是私有的,通过对外暴露调用接口与其他部分进行整合,每个块之间相互独立,但又可以互相调用和通信。

2.jQuery

  1. (function(window,undefined){
  2. var jQuery = ...
  3. window.jQuery = window.$ = jQuery
  4. })(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指令便会生成一个只读引用,等到脚本真正执行的时候,再依据这个引用到对应模块中取值。