模块化深入

装饰者(Decorator)模式:

1
// mylib/UpdatableObservable:dojo/store/Observable 的一个装饰者
define(['dojo', 'dojo/store/Observable'], function ( dojo, Observable ) {
    return function UpdatableObservable ( store ) {
        var observable = dojo.isFunction(store.notify) ? store :new Observable(store);
        observable.updated = function( object ) {
            dojo.when(object, function ( itemOrArray ) {
                dojo.forEach( [].concat(itemOrArray), this.notify, this );
            };
        };
        return observable; // 让 `new` 成为可选的
    };
});

1
// 装饰者使用者// mylib/UpdatableObservable 的一个使用者
define(['mylib/UpdatableObservable'], function ( makeUpdatable ) {
    var observable, updatable, someItem;
    // ... 获取或得到 `observable` 的代码
    // ... 让 observable store 也变得 updatable
    updatable = makeUpdatable(observable); // `new` is optional!
    // ... 之后,当一条 cometd 消息带着新的数据项到达时
    updatable.updated(updatedItem);
});

适配器(Adapter)模式

1
// 'mylib/Array' 将 `each` 函数适配为仿 jQuery 的接口:
define(['dojo/_base/lang', 'dojo/_base/array'], function (lang, array) {
    return lang.delegate(array, {
        each: function (arr, lambda) {
            array.forEach(arr, function (item, i) {
                lambda.call(item, i, item); // 就像 jQuery 的 each
            })
        }
    });
});
// 适配器使用者
// 'myapp/my-module':
define(['mylib/Array'], function ( array ) {
    array.each(['uno', 'dos', 'tres'], function (i, esp) {
        // 这里 `this` == item
    });
});

1
define(['js/jquery.js','js/jquery.color.js','js/underscore.js'], function($, colorPlugin, _){
    // 这里我们传入了 jQuery、color 插件以及 Underscore
    // 我们在全局作用域中无法访问其中的任何一个,但我们可以轻易地在
    // 伪随机生成一个颜色数组,选中打乱后数组中的第一项
    var shuffleColor = _.first(_.shuffle(['#666','#333','#111']));
    // 给页面中任意 class 带有 'item' 的元素用随机得到的颜色为
    // background-color 添加动画效果
    $('.item').animate({'backgroundColor': shuffleColor });
    return {};
    // 我们返回的东西可以被其它模块所使用
});
1
// 让文档中存在多个 jQuery 的全局实例,以便测试 .noConflict()
var jQuery = this.jQuery || "jQuery", $ = this.$ || "$",
    originaljQuery = jQuery, original$ = $,
    amdDefined;
define(['jquery'] , function ($) { $('.items').css('background','green');
return function () {}; });
// 易实现的声明支持的标志,会被 AMD 加载器所使用
define.amd = { jQuery: true };

理解 CJS:require() 与 exports// package/lib 是我们须要的一个依赖项
var lib = require(‘package/lib’);
// 我们的模块的一些行为
function foo(){
lib.log(‘hello world!’);
}
// 把 foo 导出(暴露)给其它模块
exports.foo = foo;
exports 的基本用法// 定义我们希望暴露的更多行为function foobar(){
this.foo = function(){
console.log(‘Hello foo’);
}

this.bar = function(){
        console.log('Hello bar');
}

}
// 把 foobar 暴露给其它模块
exports.foobar = foobar;
// 一个使用了 ‘foobar’ 的应用
// 相对于使用文件与模块文件所在的同一目录路径获取模块
var foobar = require(‘./foobar’).foobar,
test = new foobar();
test.bar(); // ‘Hello bar’

1



第一个 CJS 例子的 AMD 等价写法
```dash
define(['package/lib'], function(lib){
    // 我们的模块的一些行为
    function foo(){
        lib.log('hello world!');
    }
    // 把 foo 导出(暴露)给其它模块
    return {
        foobar: foo
    };
});
`

使用多个依赖项app.js
var modA = require(‘./foo’);
var modB = require(‘./bar’);
exports.app = function(){
console.log(‘Im an application!’);
}
exports.foo = function(){
return modA.helloWorld();
}
bar.js
exports.name = ‘bar’;
foo.jsrequire(‘./bar’);
exports.helloWorld = function(){
return ‘Hello World!!’’
}

1


AMD/CommonJS 通用模块定义(变体 2,UMDjs)/**
 * 如果你需要做一些循环依赖或是需要兼容非 Node 的类
 * commonjs 环境,导出基于对象的版本。
 */
```dash
(function (define) {
    //'id' 是可选的,但如果这是一个流行的 web 类库而且
    //通常被用于非 AMD/Node 的环境下,还是推荐给出。然
    //而如果希望生成匿名模块,去掉下面的 'id',并且去掉
    //define 兼容方案中使用的 id。
    define('id', function (require, exports) {
        //如果有兼容项,把它们加载进来
        var a = require('a');
        //给 exports 绑定属性。
        exports.name = value;
    });
}(typeof define === 'function' && define.amd ? define : function (id, factory) {
    if (typeof exports !== 'undefined') {
        //commonjs
        factory(require, exports);
    } else {
        //创建一个全局函数。仅在代码没有依赖项、或
        //是依赖项符合下面的调用模式时可用。
        factory(function(value) {
            return window[value];
        }, (window[id] = {}));
    }
}));

可扩展的 UMD 插件(Thomas Davis 和我自己修改的变体)core.js

1
;(function ( name, definition ){
  var theModule = definition(),
      // 这被认为是“安全”的:
      hasDefine = typeof define === 'function' && define.amd,
      // hasDefine = typeof define === 'function',
      hasExports = typeof module !== 'undefined' && module.exports;
  if ( hasDefine ){ // AMD 模块
    define(theModule);
  } else if ( hasExports ) { // Node.js 模块
    module.exports = theModule;
  } else { // 分配到常见的命名空间,或简单地分配到全局对象(window)
    (this.jQuery || this.ender || this.$ || this)[name] = theModule;
  }
})( 'core', function () {
    var module = this;
    module.plugins = [];
    module.highlightColor = "yellow";
    module.errorColor = "red";
  // 在这里定义 core 模块并返回公用 API
  // 这是 core 的方法 highlightAll() 和所有插件使用的 highlight 方法
  // 用来把元素高亮显示为不同颜色
  module.highlight = function(el,strColor){
    // 这个模块使用了 jQuery,但是老式的普通 JavaScript 或者 Dojo
    // 也可以很简单地使用。
    if(this.jQuery){
      jQuery(el).css('background', strColor);
    }
  }
  return {
      highlightAll:function(){
        module.highlight('div', module.highlightColor);
      }
  };
});

myExtension.js
;(function ( name, definition ) {
    var theModule = definition(),
        hasDefine = typeof define === 'function',
        hasExports = typeof module !== 'undefined' && module.exports;
    if ( hasDefine ) { // AMD 模块
        define(theModule);
    } else if ( hasExports ) { // Node.js 模块
        module.exports = theModule;
    } else { // // 分配到常见的命名空间,或简单地分配到全局对象(window)
        // for 循环用来处理扁平文件/全局模块扩展名
        var obj = null;
        var namespaces = name.split(".");
        var scope = (this.jQuery || this.ender || this.$ || this);
        for (var i = 0; i < namespaces.length; i++) {
            var packageName = namespaces[i];
            if (obj && i == namespaces.length - 1) {
                obj[packageName] = theModule;
            } else if (typeof scope[packageName] === "undefined") {
                scope[packageName] = {};
            }
            obj = scope[packageName];
        }
    }
})('core.plugin', function () {
    // 在这里定义你的模块并返回公用 API
    // 这段代码可以轻易地与 core 代码相适应,来允许那些覆盖/扩展 core
    // 功能的方法,让你在需要时扩展 highlight 方法来做更多事。
    return {
        setGreen: function ( el ) {
            highlight(el, 'green');
        },
        setRed: function ( el ) {
            highlight(el, errorColor);
        }
    };
});
//app.js
$(function(){
    // 'core' 插件在本例中被暴露在了一个命名空间 core 下,我们
    // 先将它缓存起来
    var core = $.core;
    // 然后使用一些原生的 core 功能来用黄色高亮页面中所有的 div
    core.highlightAll();
    // 访问被载入 core 模块的 'plugin' 命名空间的插件(扩展):
    // 把页面中的第一个 div 设为绿色背景。
    core.plugin.setGreen("div:first");
    // 这里我们通过一个在其之后加载的插件从底层使用 core 的 'highlight' 方法
    // 把最后一个 div 的背景色设为我们在 core 模块/插件中定义的
    // 'errorColor' 属性的颜色。如果你更仔细地查看代码,就会发现
    // 在 core 和其它插件间使用属性和方法是多么简单
    core.plugin.setRed('div:last');
});

1
module staff{
    // 指定其它模块可以使用的(公用)导出
    export var baker = {
        bake: function( item ){
            console.log('Woo! I just baked ' + item);
        }
    }   
}
module skills{
    export var specialty = "baking";
    export var experience = "5 years";
}
module cakeFactory{

    // 指定依赖项
    import baker from staff;

    // 通过通配符导入所有东西
    import * from skills;

    export var oven = {
        makeCupcake: function( toppings ){
            baker.bake('cupcake', toppings);
        },
        makeMuffin: function( mSize ){
            baker.bake('muffin', size);
        }
    }
}

针对服务器的类 CommonJS 模块对于面向服务器的开发者来说,在 ES.next 中提出的模块系统并非局限于对浏览器端模块的关注。通过下面的例子,你可以看见一个被提出用于服务器的类 CJS 模块:

1
// io/File.js
export function open(path) { ... };
export function close(hnd) { ... };
// compiler/LexicalHandler.jsmodule file from 'io/File';
import { open, close } from file;
export function scan(in) {
    try {
        var h = open(in) ...
    }
    finally { close(h) }
}
module lexer from 'compiler/LexicalHandler';
module stdlib from '@std';