web-dev-qa-db-ja.com

プロトタイプベースとクラスベースの継承

JavaScriptでは、すべてのオブジェクトが同時にインスタンスとクラスです。継承を行うには、任意のオブジェクトインスタンスをプロトタイプとして使用できます。

Python、C++などでは、別個の概念としてクラスとインスタンスがあります。継承を行うには、基本クラスを使用して新しいクラスを作成する必要があり、これを使用して派生インスタンスを生成できます。

JavaScriptがこの方向(プロトタイプベースのオブジェクト指向)になったのはなぜですか?従来のクラスベースのオブジェクト指向に関して、プロトタイプベースのOOの利点(および欠点)は何ですか?

192
Stefano Borini

ここには約100の用語の問題があり、その大部分は、アイデアをThe Bestのように聞こえさせようとする(あなたではなく)誰かを中心に構築されています。

すべてのオブジェクト指向言語は、いくつかの概念に対処できる必要があります。

  1. データのカプセル化、およびデータの関連操作(データメンバーとメンバー関数として、またはデータとメソッドなどとしてさまざまに知られています)。
  2. 継承、これらのオブジェクトは、これらの変更を除いて、オブジェクトの他のセットとまったく同じであると言う能力
  3. ポリモーフィズム(「多数の形状」)。オブジェクトが自身で実行するメソッドを決定するため、言語に依存してリクエストを正しくルーティングできます。

今、比較する限り:

まず、「クラス」対「プロトタイプ」の質問全体です。アイデアはもともとSimulaで始まりました。クラスベースのメソッドでは、各クラスは同じ状態空間(「可能な値」を読む)と同じ操作を共有するオブジェクトのセットを表し、等価クラスを形成しました。 Smalltalkを振り返ると、クラスを開いてメソッドを追加できるため、これはJavascriptでできることと事実上同じです。

後でOO言語は静的型チェックを使用できるようにしたかったので、コンパイル時に固定クラスセットの概念を得ました。オープンクラスバージョンでは、柔軟性が向上しました。新しいバージョンでは、テストが必要だったコンパイラでいくつかの種類の正確性をチェックする機能がありました。

「クラスベース」言語では、そのコピーはコンパイル時に行われます。プロトタイプ言語では、操作はプロトタイプデータ構造に格納され、実行時にコピーおよび変更されます。ただし、抽象的には、クラスは依然として同じ状態空間とメソッドを共有するすべてのオブジェクトの等価クラスです。プロトタイプにメソッドを追加すると、事実上、新しい等価クラスの要素が作成されます。

さて、なぜそうするのですか?これは主に、実行時にシンプルで論理的でエレガントなメカニズムを実現するためです。ここで、新しいオブジェクトを作成するには、orを使用して新しいクラスを作成します。すべてのデータとプロトタイプデータ構造をコピーして、ディープコピーを実行するだけです。継承とポリモーフィズムは多かれ少なかれ無料で取得できます。メソッド検索alwaysは、名前によるメソッド実装を辞書に要求することで構成されます。

Javascript/ECMAスクリプトになった理由は、基本的に、この10年前に始めたとき、私たちははるかに強力でないコンピューターとはるかに洗練されていないブラウザーを扱っていたからです。プロトタイプベースの方法を選択することは、オブジェクト指向の望ましい特性を維持しながら、インタープリターが非常にシンプルになることを意味しました。

190
Charlie Martin

プロトタイプベースのアプローチにわずかに偏っている比較は、論文で見つけることができます- Self:The power of simple 。この論文では、プロトタイプを支持して以下の議論を行っています。

コピーによる作成。プロトタイプから新しいオブジェクトを作成するには、単純な操作、コピー、単純な生物学的隠phor、クローン作成を行います。クラスから新しいオブジェクトを作成するには、クラス内の形式情報の解釈を含むインスタンス化を行います。インスタンス化は、計画から家を建てることに似ています。コピーは、インスタンス化よりも単純な比phorとして私たちに訴えます。

既存のモジュールの例。プロトタイプは、形式と初期化の説明ではなくオブジェクトの例であるため、クラスよりも具体的です。これらの例は、ユーザーがモジュールを理解しやすくすることで、モジュールを再利用するのに役立ちます。プロトタイプベースのシステムにより、ユーザーは典型的な代表者を説明することなく、代表者を調べることができます。

独自のオブジェクトのサポート。 Selfは、独自の動作を持つ独自のオブジェクトを簡単に含めることができるフレームワークを提供します。各オブジェクトには名前付きのスロットがあり、スロットは状態または動作を保持できるため、どのオブジェクトにも一意のスロットまたは動作を設定できます。クラスベースのシステムは、同じ動作をするオブジェクトが多数ある状況向けに設計されています。オブジェクトが独自の固有の動作を持つことは言語的にサポートされておらず、インスタンスが1つだけであることが保証されているクラスを作成するのは厄介です(Think Singleton pattern)。自己はこれらの不利益のいずれにも苦しみません。任意のオブジェクトは、独自の動作でカスタマイズできます。一意のオブジェクトは一意の動作を保持でき、個別の「インスタンス」は必要ありません。

メタ回帰の除去。クラスベースのシステムでは、オブジェクトは自給自足できません。その構造と動作を表現するには、別のオブジェクト(そのクラス)が必要です。これにより、概念的に無限のメタ回帰が発生します。pointは、クラスPointのインスタンスです。これは、メタクラスPointのインスタンスであり、メタメタクラスPointのインスタンスです。 、広告無限。一方、プロトタイプベースのシステムでは、オブジェクトに独自の動作を含めることができます。生命を吹き込む他のオブジェクトは必要ありません。プロトタイプはメタ回帰を排除します。

Self は、おそらくプロトタイプを実装する最初の言語です。 (JVMのような他の興味深い技術の先駆者でもあり、後にJVMに移行しました。したがって、 他のSelf論文 も参考になるはずです)。

38
Vijay Mathew

JavaScriptの素晴らしい本Douglas Crockford でチェックしてください。 JavaScriptの作成者が行った設計上の決定のいくつかの非常に良い説明を提供します。

JavaScriptの重要な設計上の側面の1つは、そのプロトタイプ継承システムです。オブジェクトはJavaScriptの第一級市民であるため、通常の関数もオブジェクトとして実装されます(正確には「関数」オブジェクト)。私の意見では、もともとブラウザ内で実行するように設計されていたとき、多くのシングルトンオブジェクトを作成するために使用されることを意図していました。ブラウザDOMでは、そのウィンドウ、ドキュメントなどすべてのシングルトンオブジェクトを見つけます。また、JavaScriptは緩やかに型付けされた動的言語です(強く型付けされたPythonとは対照的に)、その結果、オブジェクトの拡張の概念は 'prototype'プロパティを使用して実装されました。

そのため、JavaScriptで実装されたプロトタイプベースのOOにはいくつかの長所があると思います。

  1. 緩やかに型付けされた環境に適しており、明示的な型を定義する必要はありません。
  2. シングルトンパターンの実装を非常に簡単にします(この点でJavaScriptとJavaを比較してください。これで私が話していることがわかります)。
  3. 別のオブジェクトのコンテキストでオブジェクトのメソッドを適用し、オブジェクトなどからメソッドを動的に追加および置換する方法を提供します(強く型付けされた言語では不可能なこと)。

プロトタイプOOの短所の一部を次に示します。

  1. プライベート変数を実装する簡単な方法はありません。 Crockfordclosures を使用したウィザードを使用してプライベート変数を実装することは可能ですが、たとえばJavaまたはC#でプライベート変数を使用するほど簡単ではありません。
  2. JavaScriptに複数の継承を実装する方法がわかりません(その価値はありますが)。
23
Amit