15

JavaScriptモジュールパターンの例として、次の3つのコードブロックを見てきました。違いは何ですか?なぜ私は他のパターンよりも1つのパターンを選ぶのですか?

パターン1

function Person(firstName, lastName) {
    var firstName = firstName;
    var lastName = lastName;

    this.fullName = function () {
        return firstName + ' ' + lastName;
    };

    this.changeFirstName = function (name) {
        firstName = name;
    };
};

var jordan = new Person('Jordan', 'Parmer');

パターン2

function person (firstName, lastName) { 
    return {
        fullName: function () {
            return firstName + ' ' + lastName;
        },

        changeFirstName: function (name) {
            firstName = name;
        }
    };
};

var jordan = person('Jordan', 'Parmer');

パターン3

var person_factory = (function () {
    var firstName = '';
    var lastName = '';

    var module = function() {
        return {
            set_firstName: function (name) {
                               firstName = name;
                           },
            set_lastName: function (name) {
                              lastName = name;
                          },
            fullName: function () {
                          return firstName + ' ' + lastName;
                      }

        };
    };

    return module;
})();

var jordan = person_factory();

私の知る限り、JavaScriptコミュニティは、一般的にパターン3が最適であることに賛成しているようです。最初の2つとどう違うのですか?3つのパターンすべてを使用して、変数と関数をカプセル化できるように思えます。

注:この投稿は実際には質問に答えていません。また、重複しているとは考えていません。

4

3 に答える 3

13

私はそれらをモジュールパターンとは見なしませんが、より多くのオブジェクトインスタンス化パターンと見なします。個人的には、あなたの例はお勧めしません。主な理由は、メソッドのオーバーロード以外の関数の引数を再割り当てするのは良くないと思うからです。戻って、JavaScriptでオブジェクトを作成する2つの方法を見てみましょう。

プロトタイプとnewオペレーター

これは、JavaScriptでオブジェクトを作成するための最も一般的な方法です。これはパターン1と密接に関連していますが、毎回新しいものを作成するのではなく、オブジェクトのプロトタイプに関数をアタッチします。

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
};

Person.prototype.fullName = function () {
    return this.firstName + ' ' + this.lastName;
};

Person.prototype.changeFirstName = function (name) {
    this.firstName = name;
};

var jordan = new Person('Jordan', 'Parmer');

jordan.changeFirstName('John');

Object.createおよびファクトリ関数

ECMAScript 5では、オブジェクトをインスタンス化するさまざまな方法を可能にするObject.createが導入されました。new使用する演算子を使用する代わりにObject.create(obj)、プロトタイプを設定します。

var Person =  {
    fullName : function () {
        return this.firstName + ' ' + this.lastName;
    },

    changeFirstName : function (name) {
        this.firstName = name;
    }
}

var jordan = Object.create(Person);
jordan.firstName = 'Jordan';
jordan.lastName = 'Parmer';

jordan.changeFirstName('John');

ご覧のとおり、プロパティを手動で割り当てる必要があります。これが、最初のプロパティ割り当てを行うファクトリ関数を作成することが理にかなっている理由です。

function createPerson(firstName, lastName) {
    var instance = Object.create(Person);
    instance.firstName = firstName;
    instance.lastName = lastName;
    return instance;
}

var jordan = createPerson('Jordan', 'Parmer');

いつものように、JavaScriptオブジェクト指向プログラミングに関する最高の記事の1つであるJavaScriptOOPの理解を参照する必要があります。

また、JavaScriptの継承メカニズムを研究した後に作成したUberProtoという小さなライブラリも指摘したいと思います。これは、Object.createより便利なラッパーとしてセマンティクスを提供します。

var Person = Proto.extend({
    init : function(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    },

    fullName : function () {
        return this.firstName + ' ' + this.lastName;
    },

    changeFirstName : function (name) {
        this.firstName = name;
    }
});

var jordan = Person.create('Jordan', 'Parmer');

結局のところ、それは「コミュニティ」が何を好むように見えるかではなく、特定のタスクを達成するために言語が何を提供するかを理解することです(あなたの場合は新しいオブジェクトを作成します)。そこから、どちらの方法を好むかをより適切に決定できます。

モジュールパターン

モジュールパターンやオブジェクトの作成に混乱があるようです。似ているように見えても、責任は異なります。JavaScriptには関数スコープモジュールしかないため、機能をカプセル化するためにモジュールが使用されます(グローバル変数や名前の衝突などを誤って作成することはありません)。最も一般的な方法は、機能を自己実行関数でラップすることです。

(function(window, undefined) {
})(this);

これは単なる関数なので、最終的に何か(API)を返す方がよいでしょう。

var Person = (function(window, undefined) {
    var MyPerson = function(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    };

    MyPerson.prototype.fullName = function () {
        return this.firstName + ' ' + this.lastName;
    };

    MyPerson.prototype.changeFirstName = function (name) {
        this.firstName = name;
    };

    return MyPerson;
})(this);

これは、JSのモジュールのほとんどです。それらはラッピング関数(JavaScriptの新しいスコープと同等)を導入し、(オプションで)モジュールAPIであるオブジェクトを返します。

于 2012-12-10T19:19:59.163 に答える
4

まず、@ Daffがすでに述べたように、これらはすべてのモジュールパターンではありません。違いを見てみましょう:

パターン1とパターン2

無駄な行を省略してもよい

var firstName = firstName;
var lastName = lastName;

パターン2からわかるように、関数の引数はすでにローカルスコープの変数です。

明らかに、機能は非常に似ています。どちらも、(公開された)関数fullNamechangeFirstName関数のみがアクセスできる2つのローカル変数に対してクロージャを作成します。違いは、インスタンス化で何が起こるかです。

  • パターン2では、から継承するオブジェクト(リテラル)を返すだけですObject.prototype
  • パターン1では、「コンストラクター」と呼ばれる(また適切​​に大文字化された)関数でnewキーワードを使用します。これにより、から継承するオブジェクトが作成されPerson.prototype、すべてのインスタンスが共有する他のメソッドまたはデフォルトのプロパティを配置できます。

コンストラクターパターンには他にもバリエーションがあります。それらはオブジェクトの[public]プロパティを優先し、すべてのメソッドをプロトタイプに配置する可能性があります-そのようなものを混在させることができます。クラスベースの継承のエミュレートがどのように機能するかについては、この回答を参照してください。

いつ何を使うの?Person通常、プロトタイプパターンが推奨されます。特に、すべてのインスタンスの機能を拡張したい場合は、同じモジュールからでも拡張できない場合があります。もちろん、パターン1のユースケースもあります。特に、継承を必要としないシングルトンの場合はそうです。

パターン3

…は実際にはモジュールパターンであり、静的なプライベート変数を作成するためのクロージャーを使用しています。クロージャーからエクスポートするものは実際には無関係です。それは任意のファブリック/コンストラクター/オブジェクトリテラルである可能性があります-それでも「モジュールパターン」です。

もちろん、パターン2のクロージャは「モジュールパターン」を使用していると見なすことができますが、その目的はインスタンスを作成することなので、この用語は使用しません。より良い例は、モジュールパターンのクロージャーを使用して、コードのモジュール化に焦点を当てた、RevealingPrototypePatternまたは既存のオブジェクトを拡張するものです。

あなたの場合、モジュールは静的変数にアクセスするためのオブジェクトを返すコンストラクター関数をエクスポートします。それで遊んで、あなたはすることができます

var jordan = person_factory();
jordan.set_firstname("Jordan");
var pete = person_factory();
pete.set_firstname("Pete");
var guyFawkes = person_factory();
guyFawkes.set_lastname("Fawkes");

console.log(jordan.fullname()); // "Pete Fawkes"

これが予想されたかどうかはわかりません。もしそうなら、アクセサー関数を取得するための追加のコンストラクターは私には少し役に立たないようです。

于 2012-12-10T23:58:27.827 に答える
2

「どれが一番いいの?」ここでは、実際には有効な質問ではありません。
それらはすべて異なることを行い、異なるトレードオフがあり、異なる利点を提供します。
どちらか一方または3つすべて(またはなし)を使用するかどうかは、プログラムの設計方法に依存します。

パターン#1は、JSの伝統的な「クラス」の考え方です。

これによりprototyping、Cのような言語と混同しないようにすることができます。 プロトタイピングは、他の言語のプロパティ/メソッドに似ています。プロトタイプ化されたメソッドには、インスタンス変数(つまり、にアタッチされていない変数)へのアクセスもありません。inheritance
public staticthis

var MyClass = function (args) { this.thing = args; };
MyClass.prototype.static_public_property = "that";
MyClass.prototype.static_public_method   = function () { console.log(this.thing); };

var myInstance = new MyClass("bob");
myInstance.static_public_method();

パターン#2は、暗黙的な継承なしで、単一のオブジェクトの単一のインスタンスを作成します。

var MyConstructor = function (args) {
    var private_property = 123,
        private_method = function () { return private_property; },

        public_interface = {
            public_method : function () { return private_method(); },
            public_property : 456
        };

    return public_interface;
};


var myInstance = MyConstructor(789);

継承はなく、すべてのインスタンスが各関数/変数の新しいコピーを取得します。
1ページに数十万のインスタンスが存在しないオブジェクトを処理している場合、これは非常に実行可能です。

パターン#3はパターン#2と似ていますが、コンストラクターを構築していて、private staticメソッドと同等のものを含めることができる点が異なります(関数が目的の場合は、100%の時間、引数を渡す必要があり、returnステートメントを収集する必要があります)インスタンスコンストラクタがすべての「静的」機能にアクセスできるにもかかわらず、これらの小道具/メソッドはインスタンスレベルのデータ/機能にアクセスできないため、オブジェクトまたは配列を直接変更するのではなく、値を返します。 )。
ここでの実際的な利点は、各インスタンスがそれらの独自のコピーではなく、これらの関数への参照を持っているため、メモリフットプリントが少ないことです。

var PrivateStaticConstructor = function (private_static_args) {
    var private_static_props = private_static_args,
        private_static_method = function (args) { return doStuff(args); },

        constructor_function = function (private_args) {
            var private_props = private_args,
                private_method = function (args) { return private_static_method(args); },
                public_prop = 123,
                public_method = function (args) { return private_method(args); },

                public_interface = {
                    public_prop   : public_prop,
                    public_method : public_method
                };

            return public_interface;
        };

    return constructor_function;
};


var InstanceConstructor = PrivateStaticConstructor(123),
    myInstance = InstanceConstructor(456);

これらはすべて非常に異なることをしています。

于 2012-12-10T19:31:55.903 に答える