3

「いくつかのウェブサイト」のエスカレートするニーズを満たすために、しばらくの間 js モジュール パターンに取り組んできました。基本的に、スクリプトをグループ化/カプセル化するための適切な方法と、OO パターンの必要性が必要なだけです。

問題なく動作する構造を手に入れましたが、その特定の部分に満足していないので、リファクタリング中です。更新されたパターンは次のとおりです。

(function (global) {
    var M = {
        VERSION : 1.0
    };

    global.M = M;

    //jQ document.ready()
    global.onload = function(){
        console.log('M VERSION: %s', M.VERSION);
        var main = new M.Main();
    };

    M.Main = function(){
        var _class1;
        var _class2;
        var _class3;

        function _init(){
            _class1 = new M.Class('foo','baz');
            console.log('_class1.param2 : %s', _class1.getParam() ); //works

            _class2 = new M.OtherClass('faz','boo');
            _class2.setParam('Boozaz');
            console.log('_class2.param2 : %s', _class2.getParam() ); //works

            _class3 = new M.Class('fuz','boz')
            console.log('_class3.param2 : %s', _class3.getParam() ); //works

            _class3.prototype = new M.Super();
            console.log('_class3.__param : %s', _class3.prototype.getProtected() ) //works
        }

        _init();
        return true;
    };

    M.Super = function(){
        var __param = 'jQ';
        M.Super.API = {
            getProtected : function(){ return __param }
        }
        return M.Super.API;
    }

    M.Class = function( p1, p2){
        var _param1;
        var _param2;

        function _init(){
            _param1 = p1;
            _param2 = p2;
        }

        function _getParam(){
            return _param2;
        }

        function _setParam(e){
            _param2 = e;
        }

        M.Class.API = {
            getParam : function(){ return _getParam(); },
            setParam : function(e){ _setParam(e) },
            publicMethod : function(){  ...  }
        }

        publicMethod() //fails
        this.publicMethod() //fails, this scopes to DOM window
        M.Class.API.publicMethod() // works, but is kludgy

        _init();
        return M.Class.API;
    };

})(typeof window === 'undefined' ? this : window);

これにより、満足のいくDOM構造が生成されます(firebugによる検査時)-しかしthis、返されたオブジェクトの「パブリック」メソッドを内部的に呼び出している特定の領域の範囲を失っています。

publicMethod() //fails
this.publicMethod() //fails, this scopes to DOM window
M.Class.API.publicMethod() // works, but kludgy syntax

このパターンの前の反復では、「クラス」オブジェクトは自己実行型であり、参照thisが維持されます。

    M.Class = function( p1, p2){
        var _param1;
        var _param2;
        var _root; 

        function _init(){
            _root   = this; //this gets referenced for later usage
            _param1 = p1;
            _param2 = p2;
        }

        function _getParam(){
            return _param2;
        }

        function _setParam(e){
            _param2 = e;
        }

        M.Class.API = {
            init     : function(){ _init();    },
            getParam : function(){ return _getParam(); },
            setParam : function(e){ _setParam(e) },
        }

        console.log('getParam, internal :%s', _root.getParam() ) //success
        return M.Class.API;
    }();

  M.Class.init();

ただし、リファクタリングされたパターンでは、これらの「クラス」を を介してインスタンス化しnew、実行順序をより詳細に制御したいと考えています。

私は、js のレキシカル スコープのかなり気の遠くなるようなテーマに関する記事をたくさん読みましたが、結論は出ていません。

this更新されたモジュール パターンのスコープを維持するにはどうすればよいですか?

4

2 に答える 2

2

これは、ライブラリまたはモジュールを作成するときに誰もが尋ねる哲学的な質問の 1 つです。関数は、これまたは変数名を使用してコンテナー オブジェクトを参照する必要がありますか? 答えは、場合によります。

関数が常にthisの正しい値で呼び出されることがわかっている場合(たとえば、プロトタイプのメソッド)、thisを使用します。ただし、関数が他の方法で呼び出される可能性がある場合は、変数名を使用してください。後の段階で名前を変更することにした場合、それは非常に単純な検索と置換の演習です。また、myLib.utils.someFnを呼び出す方が、 this.someFnを呼び出すよりもはるかに明確です。入力が多すぎると感じたら、いつでもそこに戻ってvar sF = myLib.utils.someFnそこから移動できます。

編集

質問に答えるには:

publicMethod() //fails

publicMethod現在のスコープには識別子がありません。失敗します。

     this.publicMethod() //fails, this scopes to DOM window

呼び出しが M.Class() の場合、これはMへの参照です。windowを取得している場合は、別の方法で関数を呼び出しています。

     M.Class.API.publicMethod() // works, but is kludgy  

それがあなたがそれを設定した方法だからです。気に入らない場合は、別の方法で設定してください。

最後に:

)(typeof window === 'undefined' ? this : window);

Web 上で増殖しているように見える神秘的な呪文の 1 つと思われます。目的は何ですか?グローバル オブジェクトへの参照を関数に渡すことが意図されている場合は、次のようになります。

)(this);

どこでも十分です。上記の目的は、参照ウィンドウが他のオブジェクトに解決される可能性があるため、関数がグローバル オブジェクトへの参照を持つようにすることです。グローバルオブジェクトに渡されるかもしれないし渡されないかもしれないロジックを含めることは、後退したように思えます。グローバル オブジェクト自体ではなく、グローバル オブジェクトの (おそらく再割り当てされた)ウィンドウプロパティを参照する方が望ましいシナリオはどれですか?

于 2012-02-17T04:48:50.657 に答える
1

を介して手動で公開するものを除いて、すべてのメソッドが「プライベート」であるとしたらどうM.Class.APIでしょうか?

function _getParam(){
  return _param2;
}

function _setParam(e){
  _param2 = e;
}

function publicMethod(){
  console.log("public method");
}

M.Class.API = {
  getParam : _getParam,
  setParam : _setParam,
  publicMethod : publicMethod
}

publicMethod();              // succeeds
this.publicMethod();         // still fails
M.Class.API.publicMethod();  // still works, still is kludgy

また、関数から無名オブジェクトを返すと、その関数をnewキーワードで呼び出すときに意図しない結果が生じる可能性があることにも注意してください。このスタック オーバーフローの質問を参照してください。

于 2012-02-17T04:02:30.597 に答える