UMD 模块系统
鉴于存在 CommonJS、AMD 等不同的模块系统,为了让代码能够同时支持它们,社区提出了一种统一模块定义(Universal Module Definition,UMD)来解决不兼容的问题。
示例
一个常见的 UMD 模块声明实际上是一个立即执行函数表达式。模块的主体在一个工厂方法里面,其返回值作为模块最终暴露的对象。例如下面的模块暴露了一个构造函数 Time
:
;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.Time = factory();
}(this, function () {
// 模块工厂方法开始
'use strict';
var _private = '';
function Time(param) {
this._date = new Date(param);
}
return Time;
// 模块工厂方法结束
}));
分析
通常,如果一个变量在取右值时未定义,会发生引用错误(Reference Error),例如
// 标识符 an_undefined_token 不在当前作用域链上
console.log(an_undefined_token);
// 会报如下错误
// Uncaught ReferenceError: an_undefined_token is not defined
但是 typeof
运算符有所不同,typeof an_undefined_token
并不会报任何错,而是输出 undefined
。利用 JS 的这个运算符,我们可以在脚本加载后立即执行模块头部代码,利用特性检测来判断环境中存在的是哪种模块系统。
- 如果
exports
是个对象,而且module
也存在,那么运行工程函数,拿到其返回值,然后像任何一个 NodeJS 模块一样,将返回值赋给module.exports
。 - 如果
define
是个函数,而且define
上面存在amd
属性(RequireJS 的特性,表示这是一个 AMD 加载器),那么使用define
函数将模块工厂函数加到队列里。 - 如果上面两个特性检测都失败,就在全局对象上面挂载该模块的返回值。
UMD 模块试图对当前最流行的那些脚本加载器(例如 RequireJS)提供足够好的兼容性。很多情况下,它使用 AMD 为基础,并对特殊情况处理以提供 CommonJS 兼容性。