プロトタイプを使用する場合のJavascript


93

jsでプロトタイプメソッドを使用するのが適切な場合を理解したいと思います。常に使用する必要がありますか?または、それらを使用することが好ましくない場合やパフォーマンスが低下する場合がありますか?

jsでの名前空間の一般的な方法でこのサイトを検索すると、ほとんどが非プロトタイプベースの実装を使用しているようです。単にオブジェクトまたは関数オブジェクトを使用して名前空間をカプセル化します。

クラスベースの言語から来たので、類似点を描き、プロトタイプは「クラス」のようなものであり、私が言及した名前空間の実装は静的メソッドのようなものだと考えるのは難しいことではありません。

回答:


133

プロトタイプは最適化です。

それらを上手に使用する良い例は、jQueryライブラリーです。を使用してjQueryオブジェクトを取得するたびに$('.someClass')、そのオブジェクトには数十の「メソッド」があります。ライブラリはオブジェクトを返すことでそれを実現できます:

return {
   show: function() { ... },
   hide: function() { ... },
   css: function() { ... },
   animate: function() { ... },
   // etc...
};

しかし、それは、メモリ内のすべてのjQueryオブジェクトが、同じメソッドを何度も繰り返し含む数十の名前付きスロットを持つことを意味します。

代わりに、これらのメソッドはプロトタイプで定義され、すべてのjQueryオブジェクトはそのプロトタイプを「継承」して、実行時のコストをほとんどかけずにこれらすべてのメソッドを取得します。

jQueryが正しく機能するための非常に重要な部分の1つは、これがプログラマーから隠されていることです。ライブラリを使用するときに心配する必要があるものとしてではなく、純粋に最適化として扱われます。

JavaScriptの問題は、ネイキッドコンストラクター関数では、呼び出し元にプレフィックスを付けることを覚えておく必要があることですnew。そうしないと、通常は機能しません。これには正当な理由はありません。jQueryは、通常の関数の背後にそのナンセンスを隠すことによってそれを正しく$行います。そのため、オブジェクトの実装方法を気にする必要はありません。

指定したプロトタイプでオブジェクトを簡単に作成できるように、ECMAScript 5には標準関数が含まれていますObject.create。それを大幅に簡略化したバージョンは次のようになります。

Object.create = function(prototype) {
    var Type = function () {};
    Type.prototype = prototype;
    return new Type();
};

コンストラクター関数を作成し、それをで呼び出すという面倒な処理を行うだけnewです。

いつプロトタイプを避けますか?

有用な比較は、JavaやC#などの一般的なOO言語との比較です。これらは2種類の継承をサポートします。

  • インターフェイスの継承。クラスがインターフェイスのすべてのメンバーに独自の一意の実装を提供implementするinterfaceようにします。
  • 実装のあなたの継承、いくつかのメソッドのデフォルト実装を提供します。extendclass

JavaScriptでは、プロトタイプ継承は一種の実装継承です。したがって、(C#またはJavaで)基本クラスから派生してデフォルトの動作を取得するような状況では、オーバーライドを介して小さな変更を加え、JavaScriptでプロトタイプの継承が理にかなっています。

ただし、C#またはJavaのインターフェイスを使用する状況では、JavaScriptに特定の言語機能は必要ありません。インターフェイスを表すものを明示的に宣言する必要はありません。また、オブジェクトをそのインターフェイスを「実装する」としてマークする必要もありません。

var duck = {
    quack: function() { ... }
};

duck.quack(); // we're satisfied it's a duck!

言い換えると、オブジェクトの各「タイプ」に「メソッド」の独自の定義がある場合、プロトタイプから継承しても意味がありません。その後は、各タイプに割り当てるインスタンスの数によって異なります。しかし、多くのモジュール設計では、特定のタイプのインスタンスは1つしかありません。

実際、実装の継承は悪であることが多くの人から示唆されています。つまり、型にいくつかの一般的な操作がある場合、それらが基本/スーパークラスに入れられず、オブジェクトを渡すいくつかのモジュールで通常の関数として単に公開されている場合はおそらくより明確になりますあなたは彼らに働きかけたいのです。


1
良い説明。次に、プロトタイプを最適化と見なしているため、プロトタイプを常に使用してコードを改善できることに同意しますか?プロトタイプを使用しても意味がない場合や、実際にパフォーマンスが低下する場合があるのでしょうか。
2011年

フォローアップでは、「各タイプに割り当てるインスタンスの数によって異なります」と述べています。しかし、あなたが参照する例はプロトタイプを使用していません。インスタンスを割り当てる概念はどこにありますか(ここではまだ「新しい」を使用していますか)?また、quackメソッドにパラメータがあったとしましょう-duck.quack(param)を呼び出すたびに、メモリ内に新しいオブジェクトが作成されます(パラメータがあるかどうかに関係なく)
2011年

3
1.ある種類のquackアヒルのインスタンスが多数ある場合は、多くのダックインスタンスがリンクされているプロトタイプに関数が含まれるように例を変更することは意味があることを意味しました。2.オブジェクトリテラル構文{ ... }はインスタンスを作成します(これを使用newする必要はありません)。3. JSは、少なくとも一つのオブジェクトがメモリ内に作成されるようにする任意の関数を呼び出す-それは呼ばれていますarguments引数が呼び出しに渡されたオブジェクトを格納:developer.mozilla.org/en/JavaScript/Reference/...
ダニエルエリカー

ありがとうございます。しかし、私はまだあなたのポイント(1)について少し混乱しています。「1種類のアヒルの多数のインスタンス」があなたの意味を理解していません。(3)で言ったように、JS関数を呼び出すたびに、1つのオブジェクトがメモリ内に作成されます。つまり、1種類のアヒルしかなくても、ダックの関数を呼び出すたびにメモリを割り当てませんか(プロトタイプを使用することが常に意味がある場合)
2011年

11
+1 jQueryとの比較は、私が読んだプロトタイプをいつどのように使用するかについての最初の明確で簡潔な説明でした。どうもありがとうございました。
GFoley83 2013年

46

オブジェクトの「非静的」メソッドを宣言する場合は、プロトタイプを使用する必要があります。

var myObject = function () {

};

myObject.prototype.getA = function (){
  alert("A");
};

myObject.getB = function (){
  alert("B");
};

myObject.getB();  // This works fine

myObject.getA();  // Error!

var myPrototypeCopy = new myObject();
myPrototypeCopy.getA();  // This works, too.

@keatsKelleherしかし、this例のようにコンストラクタ関数内でメソッドを定義するだけで、オブジェクトの非静的メソッドを作成できますthis.getA = function(){alert("A")}か?
Amr Labib、2018年

17

組み込みprototypeオブジェクトを使用する1つの理由は、共通の機能を共有するオブジェクトを複数回複製する場合です。プロトタイプにメソッドをアタッチすることにより、newインスタンスごとに作成される複製メソッドを節約できます。ただし、メソッドをにアタッチするとprototype、すべてのインスタンスがそれらのメソッドにアクセスできます。

基本Car()クラス/オブジェクトがあるとします。

function Car() {
    // do some car stuff
}

次に、複数のCar()インスタンスを作成します。

var volvo = new Car(),
    saab = new Car();

これで、各車が運転、電源を入れる必要があることがわかりました。Car()クラスに直接メソッドをアタッチする(作成されるインスタンスごとにメモリを消費する)代わりに、プロトタイプにメソッドをアタッチできます(メソッドのみを作成します)したがって、これらのメソッドへのアクセスをnew volvoとの両方に許可しますsaab

// just mapping for less typing
Car.fn = Car.prototype;

Car.fn.drive = function () {
    console.log("they see me rollin'");
};
Car.fn.honk = function () {
    console.log("HONK!!!");
}

volvo.honk();
// => HONK!!!
saab.drive();
// => they see me rollin'

2
実際にはこれは正しくありません。volvo.honk()は、プロトタイプオブジェクトを完全に置き換えたのではなく、拡張していないため機能しません。このようなことをすると、期待どおりに機能します。Car.prototype.honk = function(){console.log( 'HONK');} volvo.honk(); // 'HONK'
29er

1
@ 29er-私がこの例を書いた方法で、あなたは正しいです。順序は重要です。この例をそのままにしておくと、このjsfiddle:jsfiddle.net/mxacAに示されCar.prototype = { ... }ているように、を呼び出す前にを呼び出す必要があります。あなたの議論については、これはそれを行う正しい方法です:jsfiddle.net/Embnp。面白いのは、この質問に答えたことを覚えていない=)new Car()
hellatan

@hellatanは、コンストラクターをCarに設定することで修正できます。プロトタイププロパティをオブジェクトリテラルで上書きしたためです。
Josh Bedo 2014年

@josh指摘してくれてありがとう。最初からそうだったはずのオブジェクトリテラルでプロトタイプを上書きしないように、回答を更新しました。
hellatan 2014

12

特定の種類のオブジェクトの多数のコピーを作成し、それらすべてが共通の動作を共有する必要がある場合は、プロトタイプオブジェクトに関数を配置します。そうすることで、各関数のコピーを1つだけ持つことでメモリを節約できますが、それは最も単純な利点にすぎません。

プロトタイプオブジェクトのメソッドを変更するか、メソッドを追加すると、対応するタイプのすべてのインスタンスの性質が即座に変更されます。

今まさに、なぜあなたは、これらすべてのことを行うだろうと、主に独自のアプリケーション設計の関数であり、物事の種類は、あなたは、クライアント側のコードで行う必要があります。(まったく別の話は、サーバー内のコードです。そこでより大規模な "OO"コードを実行することを想像するのははるかに簡単です。)


(新しいキーワードを介して)プロトタイプメソッドで新しいオブジェクトをインスタンス化すると、そのオブジェクトは各関数の新しいコピーを取得しません(単なる一種のポインタ)?この場合、プロトタイプを使用したくないのはなぜですか?
2011年

@marcel、d'oh ... =)のように
hellatan

@opiはい、そうです。コピーは作成されません。代わりに、プロトタイプオブジェクトのシンボル(プロパティ名)は、各インスタンスオブジェクトの仮想パーツとして自然に「存在」しているだけです。人々がそれを気にしたくない唯一の理由は、オブジェクトが短命で明確である場合、または共有する「動作」があまりない場合です。
ポインティ

3

クラスベースの用語で説明すると、Personはクラス、walk()はPrototypeメソッドです。つまり、walk()は、これを使用して新しいオブジェクトをインスタンス化した後でのみ存在します。

したがって、Person uが多くのユーザーを作成できるようなオブジェクトのコピーを作成する場合、メモリ内の各オブジェクトに対して同じ関数のコピーを共有/継承することでメモリを節約できるため、プロトタイプは優れたソリューションです。

一方、静的はそのようなシナリオではそれほど役に立ちません。

function Person(){
this.name = "anonymous";
}

// its instance method and can access objects data data 
Person.prototype.walk = function(){
alert("person has started walking.");
}
// its like static method
Person.ProcessPerson = function(Person p){
alert("Persons name is = " + p.name);
}

var userOne = new Person();
var userTwo = new Person();

//Call instance methods
userOne.walk();

//Call static methods
Person.ProcessPerson(userTwo);

これにより、インスタンスメソッドのようになります。オブジェクトのアプローチは静的メソッドに似ています。

https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.