メインコンテンツへ移動

モジュールパターン_JavaScript デザインパターン2

無料2015-07-12#JS#Design_Pattern#模块模式#Module_Pattern#揭示模块模式#Revealing_Pattern.高级模块模式

いわゆる Module(モジュール)パターンとは、実際には JS 向けの一種のカプセル化思想であり、多くの言語はアクセス制御メカニズム(public、private など)を提供しますが、JS にはないため、モジュールパターンが生まれました

前置き

実はここでのモジュールパターンはダグラスが提案した Module パターンで、全く同じですが、著者は主流の JS ライブラリ(フレームワーク)のモジュール管理を分析しており、これが亮点と言えます

一.モジュールパターン

JS には class の概念がなく、当然基于类のカプセル化という話もありません。class の最大の利点はおそらくモジュール(名前空間)管理とアクセス制御ですが、これらは JS にはありません。しかし JS 関数スコープの特徴を利用してアクセス制御(カプセル化)を実現できます。話すコードは以下の通り:

var module = (function() {
    // 私有属性
    var attr1 = 1;
    function fun1() {
        // ...
    }

    // 返回匿名对象,当然,也可以返回不匿名的对象
    return {
        // 公有属性
        attr2: attr1,
        fun2: fun1
    }
})();   // IIFE, 匿名函数立即执行

外部からは公有属性 attr2fun2 のみにアクセスでき、私有属性 attr1fun1 はアクセスが制限されています(undefined)。このように見ればカプセル化は良好と言えます。しかし問題は module がシングルトンであり、再利用できないことです。再利用したい場合は、少し変更する必要があります:

function Module() {
    // 私有属性
    var attr1 = 1;
    function fun1() {
        // ...
    }

    // 公有属性
    this.attr2 = attr1;
    this.fun2 = fun1;
}
var module1 = new Module();
var module2 = new Module();

アクセス制御(カプセル化)の観点から見れば、両方の方式とも可能です。第一种方式の欠点はシングルトンの制限であり、初期化時にパラメータを渡せないなどの問題も存在します。第二种方式は再利用をサポートしますが、複数のインスタンスのオーバーヘッドも存在します。具体的な使用はシーンに応じて決定すべきです

P.S.ダグラスの Module パターンに関する詳細情報は、黯羽轻扬:JS 学習ノート 3_関数式 を参照

二.Module パターンの変化

###1.ミックスインの導入

(function(m, arg){
    // ...
})(Module, arg);

これは実際にはモジュールパターンとはあまり関係がなく、重点も著者が強調する「別名」ではなく、「モジュールファイル化」中的一种重要な方法です。AMD、CMD ではすべて単一モジュールが単一ファイルに対応しており、この方式は他のファイルにあるモジュールを拡張する方法を提供します。(「ミックスインの導入」?見なかったことにしましょう。。)

上記の方式には一点問題があり、モジュールを厳密に順序通りにロードする必要があることです(まず Module オブジェクト定義をロードする必要がある)。以下の方式で制限を解除できます:

(function(m, arg){
    // ...
})(Module || {}, arg);

この方式を使用すればモジュールの非同期ロードを実現できますが、他のモジュールに依存するモジュールには適用できません

###2.導出

(function() {
    // 创建
    var obj = {};
    // 增强
    obj.attr = 1;
    // 返回
    return obj;
})();

特別なところはありません。值得一提的是ダグラスのモジュールパターン思想:作成 -> 強化 -> 返却。「導出」については、私も著者が何を言いたいのか分かりません。。

三.JS ライブラリ(フレームワーク)のモジュール管理

###1.Dojo モジュール

obj.setObject('module.submod.submod', (function() {
    // 私有属性
    // var
    // function

    return {
        // 公有属性
        // attr: val
    };
})();

非常に特別なモジュール定義方式で、setObject(str, obj) です。実はさらに便利な方式もあります:

String.prototype.ns = function (obj) {
    return setObject(this.toString(), obj);
};

時には JS が提供する柔軟性を十分に活用するのは非常に便利です(プロトタイプを拡張することによる問題を過度に心配する必要はありません。ns が衝突する可能性があるなら、regns、regNS に変えれば。。)

###2.ExtJS モジュール

Ext.namespace('module');

module.submod = (function() {
    // 私有属性
    // var
    // function

    return {
        // 公有属性
        // attr: val
    };
})();

基本的なモジュールパターンと非常によく似ており、簡単に実装できます

###3.YUI モジュール

Y.namespace('module.submod') = (function() {
    // 私有属性
    // var
    // function

    return {
        // 公有属性
        // attr: val
    };
})();

実装方式はどれも似ており、このように実装すればチェーン呼び出しをサポートします:Y.namespace('module.submod').attr = val;

###4.JQuery モジュール

function regLib(mod) {
    $(function() {
        if (mod.init) {
            mod.init();
        }
    });

    return mod;
}
var module = regLib((function() {
    // 私有属性
    // var
    // function

    return {
        // 公有属性
        // attr: val
        init: function() {
            // ...
        }
    };
})());

regLib 関数を利用して、新しいモジュールをロードする時に自動的に mod.init() を DOMready イベントハンドラに配置します。これも特徴とは言えません。実は JQuery が提供するプラグインメカニズムがモジュール管理です。ここではプラグインメカニズムを使用していませんが、JQuery 中で非プラグイン方式でカスタムモジュールを管理する方法を提供しています。JQuery プラグインメカニズムは以下の通り:

;(function ($) {
    // 私有属性
    // var
    // function

    $.fn.myPlugin = {
        // 公有属性
        // attr: val
        version: 1.0
    };
})(jQuery);

本質的な違いはなく、すべて套路です

四.Revealing Module(揭示モジュール)パターン

前面で言及したモジュールパターンとの唯一の違いは、公有属性が私有属性の参照であることを要求することです。実は第一例のコードはすでにこのようにしています。この一点だけでそれを新しいパターンとするのは価値がないかもしれないので、この部分内容はここに詰め込みました。

モジュール内部は非常に柔軟に処理でき、匿名関数の参照、私有属性の参照、または基本値を直接公開することを選択できます。何かの「揭示モジュールパターン」を満たすために、この柔軟性を放棄する必要はありません。

五.高級モジュールパターン

P.S.この部分内容は筆者が補充したもので、幸い書籍の内容が多くないため、筆者がこの記事を見つける機会がありました:深く理解する JavaScript モジュールパターン

###1.浅いコピー

上記で言及したすべての実装方式は直接モジュールオブジェクトを拡張するものです。元のモジュールオブジェクトの基礎上で独立したモジュールを作成したい場合は、浅い/深いコピーが必要です。例えば:

var MODULE_TWO = (function (old) {
    var my = {},
        key;
     
    for (key in old) {
        if (old.hasOwnProperty(key)) {
            my[key] = old[key];
        }
    }
     
    var super_moduleMethod = old.moduleMethod;
    my.moduleMethod = function () {
        // override method on the clone, access to super through super_moduleMethod
    };
     
    return my;
}(MODULE));

上記の例は浅いコピーで、参照タイプの属性は依然として共有されています。完全に独立したい場合は、深いコピー必须进行

###2.クロスファイル私有状態

これは非常に特別な方法で、[黯羽轻扬:《JavaScript 语言精粹》之函数化](/articles/《javascript 语言精粹》之函数化/) で言及した「函数化的思想」に類似しており、この基礎上にアクセス制御を追加しました。示例コードは以下の通り:

var MODULE = (function (my) {
    var _private = my._private = my._private || {},

        // 模块加载前,开启对_private 的访问,以实现扩充部分对私有内容的操作
        _unseal = my._unseal = my._unseal || function () {
            my._private = _private;
            my._seal = _seal;
            my._unseal = _unseal;
        },

        // 模块加载后,调用以移除对_private 的访问权限
        _seal = my._seal = my._seal || function () {
            delete my._private;
            delete my._seal;
            delete my._unseal;
        };
     
    // permanent access to _private, _seal, and _unseal
     
    return my;
}(MODULE || {}));

一つのモジュールがいくつかのファイルに分けられる時、モジュールロード前に _unseal を呼び出して私有属性へのアクセス権限を取得し、モジュールロード完了後に _seal を呼び出してアクセス権限を削除します。_private は《JavaScript 语言精粹》函数化部分中の my コンテナです

参考資料

コメント

コメントはまだありません

コメントを書く