プロトタイプ継承は、従来の継承と実際にどのように異なりますか?


27

継承、ポリモーフィズム、およびカプセル化は、OOPの3つの最も明確で重要な機能であり、それらから、最近では継承に高い使用統計があります。私はJavaScriptを学んでいますが、ここでは彼らはすべてプロトタイプ継承を持っていると言い、どこでも人々はそれが古典的な継承とはまったく異なるものだと言います。

しかし、実際の使用の点との違いは理解できませんか?つまり、基本クラス(プロトタイプ)を定義し、そこからいくつかのサブクラスを派生させると、どちらも基本クラスの機能にアクセスでき、派生クラスの機能を拡張できます。私が言ったことを継承の意図した結果であると考える場合、プロトタイプバージョンとクラシックバージョンのどちらを使用しているのかを気にする必要があるのはなぜですか?

さらに明確にするために、プロトタイプ継承と従来の継承の有用性と使用パターンに違いは見られません。その結果、両者が同じものであるOOADをもたらすため、それらが異なる理由を学ぶことに興味がありません。プロトタイプの継承は、実際には(理論的にではなく)古典的な継承とどの程度異なりますか?

回答:


6

JS OOに関する最近のブログ投稿

比較するのは、JavaScriptでの古典的なオブジェクト指向エミュレーション古典的なオブジェクト指向であり、もちろん違いは見られません。

免責事項:「プロトタイプOO」へのすべての参照を「JavaScriptのプロトタイプOO」に置き換えます。Selfやその他の実装の詳細は知りません。

ただし、プロトタイプOOは異なります。プロトタイプでは、オブジェクトのみがあり、オブジェクトを他のオブジェクトプロトタイプチェーンにのみ注入できます。オブジェクトのプロパティにアクセスするたびに、そのオブジェクトとプロトタイプチェーン内のオブジェクトを検索します。

プロトタイプOOの場合、カプセル化の概念はありません。カプセル化は、スコープ、クロージャー、ファーストクラス関数の機能ですが、プロトタイプのオブジェクト指向とは関係ありません。継承の概念もありません。人々が「継承」と呼ぶものは、実際には単なる多型です。

ただし、ポリモーフィズムがあります。

例えば

var Dog = {
  walk: function() { console.log("walks"); }
}

var d = Object.create(Dog);
d.walk();

明らかdにメソッドにアクセスできDog.walk、これは多型を示します。

ですから、実際には大きな違いがあります。多型しかありません。

ただし、前述のように、そうしたい場合(理由はわかりません)、JavaScriptで従来のオブジェクト指向をエミュレートし、(制限された)カプセル化と継承にアクセスできます。


1
@ Rayons、Googleはプロトタイプの継承をサポートしており、プロトタイプの継承が表示されます。Yahoo!からでも
サイードネアマティ

@Saeed継承はあいまいな用語であり、一般的に誤用されています。「継承」とはどういう意味か、「多型」とラベル付けします。定義が曖昧すぎます。
レイノス

@Rayonsはありません。プロトタイプという言葉は、プロトタイプではなく正しい用語です。私は相続については話しませんでした:)
サイードネアマティ

1
@Saeedそれは、私の頭の中にある空白のタイプミスの1つです。私はそれに注意を撫でていない
レイノス

1
うーん、用語。うん javascriptオブジェクトがプロトタイプに関連する方法を「委任」と呼びます。ポリモーフィズムは、特定の名前が異なるオブジェクトの異なるコードを指している可能性があるという事実に基づいて、下位レベルの関心事であると思われます。
ショーンマクミラン

15

古典的な継承は、状態なしで、親クラスから動作を継承します。オブジェクトがインスタンス化された瞬間の動作を継承します。

プロトタイプ継承は、親オブジェクトから動作と状態を継承します。オブジェクトが呼び出されたときの動作と状態を継承します。実行時に親オブジェクトが変更されると、子オブジェクトの状態と動作が影響を受けます。

プロトタイプ継承の「利点」は、すべてのオブジェクトがインスタンス化された後に状態と動作を「パッチ」できることです。たとえば、Ext JSフレームワークでは、フレームワークがインスタンス化された後にフレームワークのコアコンポーネントにパッチを適用する「オーバーライド」をロードするのが一般的です。


4
実際には、状態を継承している場合、あなたは傷ついた世界に自分自身を設定しています。継承された状態は、別のオブジェクトにある場合は共有されます。
ショーンマクミラン

1
javascriptには、状態とは別に動作の概念が実際にないことが私には思い浮かびます。コンストラクター関数とは別に、他の状態と同様に、すべての動作をオブジェクト作成後に割り当て/変更できます。
ジョーリSebrechts

Pythonには古典的な継承がないのですか?私が持っていてclass C(object): def m(self, x): return x*2、それからinstance = C()私が走るとき、私instance.m(3)は得る6。しかし、その後、私がCそう変更してC.m = lambda s, x: x*x、私が走るならば、私はinstance.m(3)今得る9class D(C)メソッドを作成してメソッドを変更すると、同じことが起こります。そのC場合D、変更されたメソッドを受け取るインスタンスも同様に受け取ります。私はあなたの定義に従ってPythonに古典的な継承がないことを誤解していますか?
mVChr

@mVChr:あなたはまだ動作を継承しており、その場合は状態ではなく、古典的な継承を指しますが、「オブジェクトがインスタンス化された瞬間に動作を継承する」という私の定義の問題を指摘しています。定義を修正する方法がよくわかりません。
ジョーリSebrechts

9

まず、ほとんどの場合、オブジェクトを定義するのではなく、オブジェクトを使用します。オブジェクトの使用は、両方のパラダイムの下で同じです。

2番目:ほとんどのプロトタイプ環境は、クラスベースの環境と同じ種類の区分を使用します。つまり、メソッドが継承されたインスタンス上の可変データです。したがって、再びほとんど違いはありません。(このスタックオーバーフローの質問に対する私の答えと、クラスなしのプログラムを編成するセルフペーパー参照しください。PDFバージョンについては、Citeseerを参照してください。)

第三に、Javascriptの動的な性質は、継承の種類よりもはるかに大きな影響を及ぼします。ベースオブジェクトに割り当てることにより、型のすべてのインスタンスに新しいメソッドを追加できるという事実は素晴らしいですが、クラスを再度開くことで、Rubyでも同じことができます。

4番目:実際の違いはわずかですが、使用忘れの実際的な問題newははるかに大きくなります。つまり、newプロトタイプコードと従来のコードの違いの影響を受けるよりも、aの欠落の影響を受ける可能性がはるかに高くなります。。

とはいえ、プロトタイプ継承と従来の継承の実際の違いは、保持メソッド(クラス)が保持データ(インスタンス)と同じであることです。これは、クラスを構築できることを意味します。任意のインスタンスで使用するのと同じオブジェクト操作ツールをすべて使用して、断片的に。(これは、実際、クラスエミュレーションライブラリのすべてがそれを行う方法です。他のすべてに類するアプローチではない場合は、Traits.jsを参照してください)。これは、メタプログラミングを行う場合に主に興味深いものです。


いや、最高の答えIMO!
アイバー

0

JavaScriptのプロトタイプ継承は、次の重要な点でクラスとは異なります。

コンストラクターは、関数なしで呼び出すことができる関数ですnew

function Circle (r, x, y) { 
  //stuff here
}
Var c = new Circle();
Circle.call(c, x, y, z); //This works and you can do it over and over again.

プライベート変数やメソッドはありません、せいぜいこれを行うことができます:

function Circle (r, x, y) {
  var color = 'red';
  function drawCircle () {
    //some code here with x, y and r
  }
  drawCircle();
  this.setX = function (x_) {
    x = x_;
    drawCircle();
  }

}
Circle.prototype.getX = function () {
   //Can't access x!
}

前の例では、偽のプライベート変数とメソッドに頼る場合、クラスを有意に拡張することはできません。さらに、宣言したパブリックメソッドは、新しいインスタンスが作成されるたびに再作成されます。


「クラス」を有意義に拡張できます。あなただけのローカル変数にアクセスすることはできませんcolorrxydrawCircleのレキシカルスコープにバインドされているCircle
レイノス

確かにできますが、インスタンスを作成するたびに作成されるコンテキストに追加される「パブリック」メソッドの束を作成せずに実行することはできません。
ビヨン

これは、そこで使用している非常に奇妙なパターンです。Circle.call()コンストラクタとして?「機能的なオブジェクト」(誤った呼び名)を記述しようとしているようです
ショーンマクミラン

それは私が今までやったことではありません、私はちょうど従来のクラスを持つ言語ではなくJavaScriptでコンストラクタを何度も呼び出すことが可能であると言っています。
ビヨン

1
しかし、これらの違いはどのように「重要」ですか?「新しい」を「重要」として使用せずに、関数を呼び出すことによってオブジェクトを構築することは、わずかな構文の違いではありません。同様に、プライベート変数を持たないことも根本的に違いはありませんが、わずかな違いがあります。さらに、記述した方法で、プライベート変数(に似たもの)を持つことができます。
ロエル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.