2011-01-19 35 views
73

jsでプロトタイプメソッドを使用するのが適切であることを理解したいと思います。彼らは常に使用すべきでしょうか?または、それらを使用することが好まれない、および/またはパフォーマンス上のペナルティを被る場合がありますか?Javascriptプロトタイプを使用する場合

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

クラスベースの言語から来ているので、並列を描くのは難しく、プロトタイプは「クラス」のようなものではなく、静的メソッドのように実装されています。

答えて

115

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

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

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言語との比較です。

  • インターフェース継承、どこimplementinterfaceクラスがインタフェースのすべてのメンバーのための独自の実装を提供するように、これらは、継承の2種類をサポートしています。
  • インプリメンテーションextendclassは、いくつかのメソッドのデフォルトの実装を提供します。

JavaScriptでは、プロトタイプの継承はの実装継承の種類です。したがって、(C#またはJavaでは)基本クラスから派生してデフォルトの動作を取得し、オーバーライドを使用して小さな変更を加えた場合、JavaScriptではプロトタイプの継承が有効です。

しかし、C#またはJavaでインターフェイスを使用した場合、JavaScriptでは特定の言語機能は必要ありません。オブジェクトの各「タイプ」は、独自の定義を持っている場合、つまり、

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

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

:明示的インターフェイスを表し、何かを宣言する必要、及びそのインタフェースを「実装」としてオブジェクトをマークする必要はありません"メソッド"の場合、プロトタイプから継承する価値はありません。その後、どのタイプのインスタンスをいくつ割り当てるかによって異なります。しかし、多くのモジュール設計では、特定のタイプのインスタンスが1つしかありません。

実際、it has been suggested by many people that implementation inheritance is evilです。つまり、型に共通の操作がいくつかある場合、基本/スーパークラスに入れられていない場合は明らかになりますが、オブジェクトを渡すモジュールの通常の関数として公開されているだけで、あなたはそれらを操作したい。

+1

良い説明。プロトタイプを最適化することを考えているので、コードを改善するために常に使用できます。プロトタイプを使用することが意味をなさない場合や実際にパフォーマンスの低下を招くケースがあるかどうかは疑問です。 – opl

+0

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

+3

** 1。**あるタイプのアヒルのインスタンスが多数存在する場合、 'quack'関数がプロトタイプになるようにサンプルを変更すると意味があります。インスタンスはリンクされています。 ** 2 ** **オブジェクトのリテラル構文 '{...}'はインスタンスを作成します(それには 'new'を使う必要はありません)。 ** 3。**任意の関数JSを呼び出すと、メモリに少なくとも1つのオブジェクトが作成されます。これは 'arguments'オブジェクトと呼ばれ、呼び出しで渡された引数を格納します:https://developer.mozilla.org/ja/JavaScript/Reference/functions_and_function_scope/arguments –

11

特定の種類のオブジェクトのコピーをたくさん作成するときに関数をプロトタイプオブジェクトに置き、共通の動作を共有する必要があります。そうすれば、各機能のコピーを1つだけ持つことでメモリを節約できますが、それは最も簡単なメリットです。

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

正確にはなぜであるかは、ほとんどすべてあなた自身のアプリケーション設計とクライアント側コードで行う必要のある種類の機能です。 (全く別の話はサーバー内のコードであり、そこではもっと大規模な "OO"コードを考えるのがはるかに容易である)

+0

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

+0

@marcel、d'oh ... =) – hellatan

+0

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

1

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

Personのようにオブジェクトのコピーを作成したい場合は、多くのユーザーを作成することができます。プロトタイプはメモリ内のオブジェクトごとに同じコピーの機能を共有/継承することによってメモリを節約するので、

静的は、このようなシナリオではあまり役に立ちません。

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

40

オブジェクトの「非静的」メソッドを宣言する場合は、プロトタイプを使用する必要があります。あなたが共通の機能を共有するオブジェクトを複数回複製されます場合は、組み込みのprototypeオブジェクトを使用する

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. 
+4

+1いいですね。それを考慮していなかった。 – GFoley83

+0

@keatsKelleherしかし、このthis.getA = function(){alert( "A")} 'を使ってコンストラクタ関数内でメソッドを定義するだけで、オブジェクトの非静的メソッドを作成できます。 –

13

一つの理由です。プロトタイプにメソッドをアタッチすることによって、newインスタンスごとに作成されるメソッドの複製を保存することができます。しかし、prototypeにメソッドをアタッチすると、すべてのインスタンスがそれらのメソッドにアクセスできます。

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

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

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

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

さて、あなたは知っている、各車が代わりに(作成された各インスタンスごとにメモリを占有)Car()クラスにメソッドを直接取り付ける、あなたはメソッドにを添付することができますなど、オン、駆動する必要があります。代わりにプロトタイプを作成して(メソッドを1回だけ作成する)、新しいメソッドvolvosaabの両方にアクセスできます。

+2

実際これは間違っています。 prototypeオブジェクトを完全に置き換えて拡張しないため、volvo.honk()は機能しません。このようなことをするなら、期待どおりに動作します:Car.prototype.honk = function(){console.log( 'HONK');} volvo.honk(); // 'HONK' – 29er

+1

@ 29er - この例を書いた方法では、あなたは正しいです。順序は重要です。この例をそのまま残しておけば、このjsfiddleに示すように 'Car.prototype = {...}'は 'new Car()'を呼び出す前に来なければなりません:http://jsfiddle.net/mxacA /。あなたの主張については、これは正しい方法です:http://jsfiddle.net/Embnp/。面白いのは、私はこの質問に答えて覚えていません=) – hellatan

+0

@hellatanあなたはオブジェクトリテラルでプロトタイププロパティを上書きしたので、コンストラクタ:Carを設定することで修正できます。 –

関連する問題