介绍
多年来,可供选择的JavaScript组件的生态系统不断地稳步增加。有很多的选择固然是很好的一件事,但是各个组件混合搭配使用的时候会带来不少的问题,开发者不会花很多时间就会发现所有组件使用起来总有这样那样的问题。
为了解决这些问题,互为竞争对手的模块规范 AMD 和 CommonJS 出现了,它们可以让开发者在约定的沙箱以模块化的方式编写自己的代码,以免“污染生态系统”。
AMD
异步模块定义(英文简称AMD)已经引领了前端潮流,RequireJS已经是最流行的实现方式。
下面的例子是
foo模块简单地依赖 jquery。
define(['jquery'], function ($) { function myFunc(){};
return myFunc; });
下面的更复杂一点的例子就是多个依赖和多个暴露方法的用法。
define(['jquery', 'underscore'], function ($, _) {
function a(){}; function b(){}; function c(){};
return { b:b, c:c } });
定义的第一部分是依赖的数组,而第二部分基本上是仅在第一部分声明好才能执行的回调函数。(像 RequireJS 这种脚本加载器才会关心这部分,包括找出依赖文件的位置)
注意:定义中的依赖顺序很重要!(比如
jQuery--->$,underscore--->_)
还要注意的是,我们可以映射依赖到我们想要的变量上。如果我们将上面代码中的
$改为$,那我们下面代码的函数块中引用到jQuery时都得用$代替$。
最重要的一点是:你绝对不能在上述代码外的函数中引用变量
$和_,因为它对于外面来说就是一个不透明的沙箱。这就是那些规范想要达到的目标!
CommonJS
如果你用过
Node.js写过代码,那你会对CommonJS感到熟悉(因为就是只有一些轻微的变动)。它已经变成使用Browserify开发的前端开发者中的一种趋势。
用跟上面一样的格式,下面就是采用
CommonJS规范的foo模块写法。
var $ = require('jquery'); function myFunc(){ //... }; module.exports = myFunc;
下面是应用了多依赖和多个暴露方法的复杂例子:
var $ = require('jquery'); var _ = require('underscore'); function a(){}; function b(){}; function c(){}; module.exports = { b: b, c: c };
UMD: 通用模块定义
虽然
CommonJS和AMD的风格同样大受欢迎,但是看起来似乎它们并没有达成共识。这样的局面也导致了一种能同时支持两种风格的需要出现,这带给了我们通用模块定义。
下面这种模式诚然丑陋,但是能使
AMD和CommonJS和谐相处,还支持老式的global变量定义。
(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD define(['jquery'], factory); } else if (typeof exports === 'object') { // Node, CommonJS-like module.exports = factory(require('jquery')); } else { // Browser globals (root is window) root.returnExports = factory(root.jQuery); } }(this, function ($) { // methods function myFunc(){}; // exposed public method return myFunc; }));
保持同样的模式实现更复杂的例子:
(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD define(['jquery', 'underscore'], factory); } else if (typeof exports === 'object') { // Node, CommonJS-like module.exports = factory(require('jquery'), require('underscore')); } else { // Browser globals (root is window) root.returnExports = factory(root.jQuery, root._); } }(this, function ($, _) { // methods function a(){}; // private because it's not returned (see below) function b(){}; // public because it's returned function c(){}; // public because it's returned // exposed public methods return { b: b, c: c } }));