web-dev-qa-db-ja.com

コンストラクター関数とプロトタイプでのJavaScriptオブジェクトメソッドの宣言

Javascriptオブジェクトの作成では、コンストラクター関数またはプロトタイプにメソッド宣言を配置できます。たとえば、NameプロパティとBarkメソッドを持つDogクラスが必要だとします。 Barkメソッドの宣言をコンストラクター関数に入れることができます。

var Dog = function(name) {
    this.Name = name;
    this.Bark = function() {
        alert(this.Name + " bark");
    };
}

または、プロトタイプオブジェクトのメソッドとして入力できます。

var Dog = function(name) {
    this.Name = name;
}

Dog.prototype.Bark = function() {
    alert(this.Name + " bark");
};

タイプDogのオブジェクトをインスタンス化すると、両方のアプローチがうまくいくようです:

var dog = new Dog("Fido");
dog.Bark();  //Both approaches show "Fido bark"

これらのアプローチの一方を他方よりも優先すべきですか?一方を他方に使用する利点はありますか?舞台裏では、これら2つのアプローチはまったく同じことをしているのでしょうか?ほとんどの人はどのアプローチを好む傾向がありますか?

助けてくれてありがとう。

145
Joe Alfano

あなたが与える例では、プロトタイプのアプローチを使用する必要があります。一般に、それは依存します。最初のアプローチ(コンストラクターでメソッドを初期化する)の主な利点は、コンストラクター内でメソッドで定義されたローカル変数を利用することでクロージャーを利用できることです。これらの変数は、コンストラクター関数の外部から直接アクセスできないため、事実上「プライベート」です。つまり、これらの変数がオブジェクトのプロパティとして定義されている場合よりもAPIがクリーンになります。一般的な経験則:

  • メソッドがコンストラクターで定義されたローカル変数を使用しない場合(例では使用しません)、プロトタイプアプローチを使用します。
  • Dogsを大量に作成する場合は、プロトタイプのアプローチを使用してください。このように、すべての「インスタンス」(つまり、Dogコンストラクターによって作成されたオブジェクト)は1つの関数セットを共有しますが、コンストラクターの方法では、Dogコンストラクターが呼び出されるたびに新しい関数セットが作成されます、より多くのメモリを使用しています。
  • 少数のDogsを作成していて、コンストラクターでローカルの「プライベート」変数を使用するとコードが改善されることがわかった場合は、これがより良いアプローチかもしれません。パフォーマンスまたはメモリ消費が大きな懸念事項である場合は、判断していくつかのベンチマークを実行してください。

ローカルプライベートコンストラクター変数にアクセスする必要があるメソッドのみがコンストラクターで定義され、他のメソッドがプロトタイプに割り当てられるハイブリッドアプローチを使用することができます。

たとえば、次のコードはコンストラクターでローカル変数を使用して、実際の数をプライベートに保ちながらこの犬がdogえた回数を追跡するため、barえ関連のメソッドはコンストラクター内で定義されます。尾を振るには、樹皮の数にアクセスする必要がないため、そのメソッドはプロトタイプで定義できます。

var Dog = function(name) {
    this.name = name;

    var barkCount = 0;

    this.bark = function() {
        barkCount++;
        alert(this.name + " bark");
    };

    this.getBarkCount = function() {
        alert(this.name + " has barked " + barkCount + " times");
    };
};

Dog.prototype.wagTail = function() {
    alert(this.name + " wagging tail");
};

var dog = new Dog("Dave");
dog.bark();
dog.bark();
dog.getBarkCount();
dog.wagTail();
213
Tim Down

2つは異なります。最初のメソッドはプロトタイプオブジェクトのメソッドonlyへの参照を格納しますが、2番目のソリューションはオブジェクトのeachにメソッドを格納します。これは、各オブジェクトに追加のポインターが含まれるため、それぞれより多くのメモリを占有することを意味します。

オブジェクトごとのメソッドにより、メソッドはコンストラクター内の変数を参照できます(クロージャー)。したがって、プロトタイプメソッドからはアクセスできないデータにアクセスできます。

最後に、プロトタイプメソッドは後で変更にできます。つまり、Barkを実行時にプロトタイプオブジェクトで再定義できます。この変更は、このプロトタイプを持つすべてのオブジェクトに対して機能します(メソッドは常にプロトタイプを調べました)。

11
Mathias Schwarz

私が見たJavaScriptコードの大部分は、プロトタイプメソッドを使用しています。これには3つの理由があると思います。

1つ目は、すべてのクラスが巨大なコンストラクターになることを避けることです:コンストラクターロジックはコンストラクター関数に入り、他のメソッドのロジックは他の場所で宣言されます-これは主に明確なこと/懸念の分離のものですが、javascriptではすべてが必要です明快さのあなたはあなたの手を得ることができます。

2つ目は効率です。コンストラクターでメソッドを宣言すると、オブジェクトの各インスタンスに対して関数オブジェクトの新しいインスタンスが作成され、コンストラクターのスコープがこれらの各関数にバインドされます(つまり、たとえば、コンストラクタへの引数。オブジェクトが存続している限り、コンストラクタをgcすることはできません)。プロトタイプでメソッドを宣言すると、すべてのインスタンスで使用される関数オブジェクトの単一のコピーがあります。プロトタイプのプロパティはインスタンスにコピーされません。

3番目の理由は、Backbone.jsで使用されるプロトタイプチェーンやCoffeeScriptのクラス構成など、プロトタイプメソッドを使用すると、さまざまな方法でクラスを「拡張」できることです。

9
jjm