web-dev-qa-db-ja.com

モジュールパターン-1つのモジュールのコードを異なるjsファイルに分割する方法は?

モジュールパターンの場合、次のようなことをしています。

_(function(namespace) {
    // tons of code
    // blabla
})(window.myGlobalNamespace);
_

コードを分割するにはどうすればよいですか?名前空間の階層を使用したり、window.myGlobalNamespace.additionalFunc = function () {//blabla}によってオブジェクトを外部に展開したりするなど、いくつかの方法を考えることができます。他の方法は何ですか?長所と短所は何ですか?どちらがより良い習慣と考えられていますか?

2つの答えはどちらもRequireJSを示唆しています。 RequireJSがこれらの問題をどのように解決できるか説明していただけますか?

first.js:

_(function(context) {
    var parentPrivate = 'parentPrivate';
})(window.myGlobalNamespace);
_

second.js:

_(function(context) {
    this.childFunction = console.log('trying to access parent private field: ' + parentPriavte);
}(window.myGlobalNamespace.subNamspace);
_

main.js:

_window.myGlobalNamespace.subNamspace.childFunction(); // doesn't work
_

そして人々はできる

_window.myGlobalNamespace.subNamspace.childFunction = function() {alert("a new function");}
_

私のコードの振る舞いを変えるために!

ここでは、2つの問題があります。

  1. 子供がアクセスできるが、外部の人はアクセスできない(つまり保護されている)フィールドを作成することはできません。それを達成する方法はありますか?

  2. そうでない場合、つまり、eparentPrivateにアクセスできるようにする場合は、公開する必要があります。その後、ユーザーはそれを変更できるようになります!

さらに、すべてのパブリック関数を変更および置換できます。私はそれが起こらないようにしたい。

RequireJSがこれらの問題をどのように解決するのかわかりません。誰かが光を当てることはできますか?

11
Boyang

JavaScriptをHTMLに取り込む方法は2つしかありません。

  1. インライン-<script> some JavaScript </script>
  2. リンク-<script src='main.js'></script>

これは明らかですが、次に来るものには共通の基盤が必要です。 ;)

JavaScriptには、他のJavaScriptファイルを自分自身に「インポート」する機能はありません。すべての「インポート」はHTMLで行われます。これはいくつかの方法で実行できます。

  • リンクそれぞれを個別にHMTLにリンク
  • JavaScriptを介して動的にリンク

    var script = document.createElement("script");
    script.src = "all.js";
    document.documentElement.firstChild.appendChild(script);
    
  • Librarylike RequireJS 。 RequireJSは 非同期モジュール定義(AMD)API を使用します。これは、モジュールとその依存関係を非同期にロードできるようにモジュールを定義するためのJavaScriptメカニズムです。

JavaScriptを別々のファイルに分割する理由を検討することは重要です。

  • 保守性-一度に1つのピースで作業するのが簡単になります
  • 読みやすさ-すべてが1つの大きなファイルにある場合、何が何であるかを確認するのは非常に困難です
  • 分業-1つの大きなファイルではなく、複数の開発者が複数のファイルで作業する方が簡単です
  • 再利用-すべての関数を 非常にまとまりのある モジュールに分割できます

個別のJavaScriptファイルしないでください物事を作成しますプライベート、クロージャは物事をプライベートにします。

さて、一日の終わりにすべてが本番環境の準備ができたら、あなたができる最善のことは 最適化 JavaScriptをすべて1つのファイルに結合して、ユーザーがダウンロードできるファイルが1つだけになるようにすることです。


JavaScriptでプライベート変数を扱うとき、ある時点でそれらにアクセスしたくなるでしょう。

  • Public関数変更にすることができます。
  • Privilegedfunction --a PublicPrivate変数にアクセスできる関数。
  • ただし、関数がインスタンスにある場合、各オブジェクトでのみ変更できます

いくつかのコードで説明しましょう。

module-test.htmlおよびmain.js(テストを容易にするためにfirst.js、second.js、およびmain.jsをマージ)

var MODULE = (function () {
        //Private variables
        var privateParent,
            app;
        
        privateParent = 'parentPrivate';
        
        return app = {
                //Privileged method
                getPrivateParent: function() {
                        return privateParent;
                }
        };
}());

MODULE.sub = (function (parentApp) {
        //Private variables
        var childMessage,
            Constr;
        
        childMessage = ' - trying to access parent private field: ' + parentApp.getPrivateParent();  //prints parentPrivate

        Constr = function () {
                this.childF = this.childFunction();
        };
        
        //Constructor
        Constr.prototype = {
                constructor: MODULE.sub,
                version: "1.0",
                childFunction: function () {
                        $("#testing-div").append(childMessage + "</br>");
                }
        };
        return Constr;
        
}(MODULE));
        
//We could just as easily print to the console, but the 'append' allows us to display the results on the page.

$("#testing-div").append("This first part shows what <b>does not work</b>; everything is 'undefined'. " + "</br>");
$("#testing-div").append("You are unable to access the var or func directly. " + "</br>");
$("#testing-div").append("MODULE.privateParent = " + MODULE.privateParent + "</br>");
$("#testing-div").append("MODULE.app = " + MODULE.app + "</br>");
$("#testing-div").append("MODULE.sub.childMessage = " + MODULE.sub.childMessage + "</br>");
$("#testing-div").append("MODULE.sub.Constr = " + MODULE.sub.Constr + "</br>");
$("#testing-div").append("MODULE.sub.childFunction = " + MODULE.sub.childFunction + "</br>");
$("#testing-div").append("END lesson. You must access childFunction() through the <b>new</b> operator." + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
        
$("#testing-div").append("Let's see if making an instance of the Object works" + "</br>");
var test = new MODULE.sub();
test.childFunction(); //run the method
$("#testing-div").append("Looks like it did!!!!" + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
        
$("#testing-div").append("Now let's try to change the childFunction() ?" + "</br>");
test.childFunction = function() {$("#testing-div").append(" - This is a new function." + "</br>");}
test.childFunction(); // altered version
$("#testing-div").append("Looks like it was changed. :(" + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
$("#testing-div").append("Does it stay changed?" + "</br>");
var test2 = new MODULE.sub();
test2.childFunction(); // doesn't work
$("#testing-div").append("NO, it was only Overriden in the 'test' Object.  It did not effect all the other new objects. :)" + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Module Test</title>
<!--         <script data-main="scripts/main" src="scripts/require.js"></script> -->
</head>
<body>
    <h1>This is a test for separate Modules and Private variables.</h1>
    <div id="testing-div">
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="main.js"></script>
</body>
</html>

RequireJSを使用して上記を実行する場合は、それが可能です。 RequireJSは、あなたと私がすでに使用しているモジュールパターンを使用します。ファイルを分離したい場合は、これを行うには2つの方法があります。

  1. Normal-RequireJSを使用するようにJSファイルを設定し、わずかな変更を加えるだけで上記のモジュールにドロップします。
  2. Leveraged-クロージャを設定するためのモジュールとして、RequireJSのモジュールの性質を使用します。これは理解するのが難しいように見えますが、長期的にはより効率的かもしれません。

注:これら2つのオプションを比較する機会はまだありませんが、完全を期すためにそれらを含めたいと思いました。


次の参考資料が役立つ場合があります。

38
Joshua Wilson

あなたが求めているのはRequireJSのようです。現在、ほとんどすべてのビルドでこれを使用しています。あるいは、明らかになるモジュールパターンをフォールバックとして見ることもできますが、その後の目的には、Requireの方がはるかに適しているように聞こえます。

http://requirejs.org

良い読み物: http://net.tutsplus.com/tutorials/javascript-ajax/principles-of-maintainable-javascript/

3
leaksterrr

Javascriptの保護変数は、保護変数を渡すことで実現できます 依存関係として 。サブクラスは、保護された変数にアクセスできるのは親内にあるため、親内に作成する必要があります。 jsFiddleの例

App = window.App || {};
App.ParentClass = (function(){

   var protectedState = {
      protectedVar: 'wow such protection'
   }

   return{
      SubClassInstance: App.SubClass(protectedState), //make the subclass accessible from outside
   }
})(); //closure gives us privacy

SubClass.js

App = window.App || {};
App.SubClass = function(protectedState){

    return {
        logProtectedVar : function(){
            //can access protectedState of parent
            console.log(protectedState.protectedVar);
        }
    }
}// I require protected state to work

main.js

// protected variable is accessible from parent and subclass and nowhere else

App.ParentClass.SubClassInstance.logProtectedVar();
// 'wow such protection' 

注:Charles W.が述べたように、このパターンはprotectedStateがオブジェクトである場合にのみ機能します。それがstring/intの場合、値によって渡され、サブクラスで行われた変更は親のコピーからは見えません。

3
roo2

モジュール化(コードの分割)は、データ保護(データの非表示)と同じではありません。

RequireJSは、データ保護の問題ではなく、モジュール化の問題を解決します。言い換えれば...データを保護しようとする際に存在する問題やデータを保護するために存在する解決策は何でもこれらの問題と解決策はRequireJSの有無にかかわらず同じです。

RequireJSは、モジュール間の依存関係を指定し、必要に応じてこれらの依存関係onlyをロードし、すでにロードされているものを再ロードしないようにし、不要なものをロードしないようにするためのすべてのメカニズムを実装します、モジュールの場所をすばやく変更したり、冗長性を持たせたりします。

デプロイ後、RequireJSがどういうわけか重すぎると感じた場合は、代わりに使用できる アーモンド ライブラリがあります。

子供がアクセスできるが、外部の人はアクセスできない(つまり保護されている)フィールドを作成することはできません。それを達成する方法はありますか?

モジュール化が必要な場合(つまり、子を親とは別にコーディングしたい場合)、JavaScriptではこれが可能であるとは思いません。子と親を同じクロージャで動作させることは可能ですが、これはモジュラーではありません。これは、RequireJSの有無にかかわらず当てはまります。

そうでない場合、つまり、eparentPrivateにアクセスできるようにする場合は、公開する必要があります。その後、ユーザーはそれを変更できるようになります!

parentPrivateへの割り当てを防ぎたい場合は、parentPrivateを含む名前空間で Object.freeze() を使用できます。

しかし、それがさまざまなブラウザでどれだけうまくサポートされているかはわかりません。また、parentPrivateにあるもの自体がプリミティブ値ではなくオブジェクトである場合、コードのクライアントによって変更されたくない場合は、フリーズする必要もあります。また、オブジェクトがフリーズされると、すべてのユーザーに対してフリーズされるため、オブジェクトを所有するモジュールは、オブジェクトを変更するための特別な処理を受けません。そして、フリーズは何も隠しません

または、次のRequireJSの例のようにセッターとゲッターを使用できます。

define(function () {
    'use strict';

    var writable = "initial value";

    var namespace = {
        get unwritable() { return writable; },
        doSomething: function () { writable = "changed value"; }

    };

    return namespace;

});

モジュールがparentとしてインポートされた場合、parent.unwritableに書き込むことはできませんが、モジュール自体はwritableに書き込むことによって返される値を変更できます。ゲッターによって返される戻り値がプリミティブ値ではなくオブジェクトである場合、このオブジェクトは呼び出し元によって変更できることに注意してください。

繰り返しますが、これはRequireJSを使用するかどうかに関係なく当てはまります。解決策は同じ、問題は同じなどです。

1
Louis