16.2 JavaScript 中的模块
虽然 JavaScript 从来没有内置的模块,但是社区聚集于一种简单的模块风格,此模块风格在 ES5 和更早版本中通过库来实现。在 ES6 中也同样接受这种风格:
- 每一个模块都是一块代码,在加载完成后执行。
- 在这段代码中,可能有各种声明(变量声明,函数声明等等)。
- 默认情况下,这些声明只是模块本地的。
- 你可以把其中一些标记为导出,这样其它模块就可以引入了。
- 一个模块不仅能够导出东西,也可以从其它模块中导入东西。可以通过模块标识符或者字符串访问那些模块:
- 相对路径(‘
../model/user
’):这些路径相对于当前模块。通常文件扩展名.js
可以省略。 - 绝对路径(‘
/lib/js/helpers
’):直接指向要引入的模块文件。 - 名字(‘
utils
’):需要配置模块名和所指向模块的映射关系。
- 相对路径(‘
- 模块是单例的。即便一个模块被引入了多次,仅会存在该模块的单一“实例”。
- 一个客户端或服务器应用的执行开始于一个初始化模块。在一些模块系统里面(包括 ES6 ),初始化模块可以内嵌在 HTML 文件中(考虑 <script> 元素)。
这种模块形式避免了全局变量,唯一全局的东西就是模块标识符。
16.2.1 ES5 模块系统
在语言层面上没有显示支持的前提下, ES5 模块系统的良好运作令人印象深刻。两个重要的标准(很不幸两者不兼容)是:
- CommonJS 模块 : 该规范主要实现于 Node.js 中( Node.js 模块有一些特性超越了 CommonJS )。特点:
- 紧凑的语法
- 为同步加载设计
- 主要用于服务器端
- 异步的模块定义( AMD ): 该标准最流行的实现是 RequireJS。特点:
- 稍微复杂的语法,使 AMD 能够不借助于 eval() (或者说是一个编译步骤)。
- 为异步加载设计
- 主要用于浏览器端
上面是目前情况的一个简单说明。如果需要更深入的资料,可以参考 Addy Osmani 的 《 Writing Modular JavaScript With AMD, CommonJS & ES Harmony 》。
16.2.2 ECMAScript 6 模块
ECMAScript 6 模块的目标是创建一种模块化方式,让 CommonJS 使用者和 AMD 使用者都乐于接受:
- 类似于 CommonJS ,语法紧凑,倾向于导出单个内容,支持循环依赖。
- 类似于 AMD ,直接支持异步加载和可配置模块加载。
通过语言层面上的支持, ES6 模块超越了 CommonJS 和 AMD (后面详细解释):
- 语法比 CommonJS 更加紧凑。
- 可以静态分析代码结构(比如静态检测,优化,等等)。
- 比 CommonJS 更好地支持循环依赖。
ES6 模块标准有两个部分:
- 声明式的语法(在引入和导出的时候)
- 可编程的加载器 API :可以配置模块如何加载,并且可以按条件加载模块