質問で示した両方のコードサンプルは、プロトタイプの継承を利用しています。実際、JavaScriptで作成するオブジェクト指向コードはすべて、プロトタイプ継承のパラダイムです。JavaScriptには従来の継承はありません。これにより、問題が少し解消されます。
Inheritance
|
+-----------------------------+
| |
v v
Prototypal Classical
|
+------------------------------+
| |
v v
Prototypal Pattern Constructor Pattern
ご覧のとおり、プロトタイプ継承とクラシック継承は、継承の2つの異なるパラダイムです。Self、Lua、JavaScriptなどの一部の言語は、プロトタイプの継承をサポートしています。ただし、C ++、Java、C#などのほとんどの言語は、従来の継承をサポートしています。
オブジェクト指向プログラミングの概要
プロトタイプ継承とクラシック継承はどちらもオブジェクト指向プログラミングのパラダイムです(つまり、オブジェクトを扱います)。オブジェクトは、実際のエンティティのプロパティをカプセル化した単なる抽象化です(つまり、オブジェクトはプログラム内の実際の単語を表します)。これは抽象化として知られています。
抽象化:コンピュータプログラムでの現実世界のものの表現。
理論的には、抽象化は「特定の例から共通の特徴を抽出することによって形成される一般的な概念」として定義されます。ただし、この説明のために、代わりに前述の定義を使用します。
現在、いくつかのオブジェクトには多くの共通点があります。例えば、マッドバイクとハーレーダビッドソンには多くの共通点があります。
マッドバイク:
ハーレーダビッドソン:
マッドバイクとハーレーダビッドソンはどちらもバイクです。したがって、バイクはマッドバイクとハーレーダビッドソンの両方を一般化したものです。
Bike
|
+---------------------------------+
| |
v v
Mud Bike Harley Davidson
上記の例では、バイク、マッドバイク、ハーレーダビッドソンはすべて抽象化されています。ただし、自転車は、マッドバイクとハーレーダビッドソンのより一般的な抽象化です(つまり、マッドバイクとハーレーダビッドソンの両方が特定の種類の自転車です)。
一般化:より具体的な抽象化の抽象化。
オブジェクト指向プログラミングでは、オブジェクト(実世界のエンティティの抽象化)を作成し、クラスまたはプロトタイプを使用して、これらのオブジェクトの一般化を作成します。汎化は継承によって作成されます。バイクはマッドバイクを一般化したものです。したがって、泥自転車は自転車から継承します。
古典的なオブジェクト指向プログラミング
従来のオブジェクト指向プログラミングでは、クラスとオブジェクトの2種類の抽象化があります。前述のように、オブジェクトは実世界のエンティティを抽象化したものです。一方、クラスはオブジェクトまたは別のクラスの抽象化です(つまり、それは一般化です)。たとえば、次のことを考慮してください。
+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | Man | Class of object johnDoe. |
| 3 | Human | Superclass of class Man. |
+----------------------+----------------+---------------------------------------+
古典的なオブジェクト指向プログラミング言語でわかるように、オブジェクトは抽象化(つまり、すべてのオブジェクトの抽象化レベルは1)であり、クラスは一般化(つまり、すべてのクラスの抽象化レベルは1より大きい)です。
従来のオブジェクト指向プログラミング言語のオブジェクトは、クラスをインスタンス化することによってのみ作成できます。
class Human {
// ...
}
class Man extends Human {
// ...
}
Man johnDoe = new Man();
古典的なオブジェクト指向プログラミング言語の要約では、オブジェクトは実世界のエンティティの抽象化であり、クラスは一般化(つまり、オブジェクトまたは他のクラスの抽象化)です。
したがって、抽象化のレベルが上がるとエンティティはより一般的になり、抽象化のレベルが下がるとエンティティはより具体的になります。この意味で、抽象化のレベルは、より具体的なエンティティからより一般的なエンティティまでのスケールに類似しています。
プロトタイプのオブジェクト指向プログラミング
プロトタイプのオブジェクト指向プログラミング言語は、従来のオブジェクト指向プログラミング言語よりもはるかに単純です。これは、プロトタイプのオブジェクト指向プログラミングでは、1つのタイプの抽象化(つまり、オブジェクト)しかないためです。たとえば、次のことを考慮してください。
+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | man | Prototype of object johnDoe. |
| 3 | human | Prototype of object man. |
+----------------------+----------------+---------------------------------------+
プロトタイプのオブジェクト指向プログラミング言語でわかるように、オブジェクトは実世界のエンティティ(この場合は単にオブジェクトと呼ばれる)または他のオブジェクト(この場合は抽象化されるオブジェクトのプロトタイプと呼ばれる)の抽象化です。したがって、プロトタイプは一般化されたものです。
プロトタイプのオブジェクト指向プログラミング言語のオブジェクトは、ex-nihilo(つまり、何もない状態)または別のオブジェクト(新しく作成されたオブジェクトのプロトタイプになる)から作成できます。
var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);
私の控えめな意見では、プロトタイプのオブジェクト指向プログラミング言語は、古典的なオブジェクト指向プログラミング言語よりも強力です。
- 抽象化のタイプは1つだけです。
- 汎化は単なるオブジェクトです。
ここまでで、古典的継承とプロトタイプ継承の違いを理解しているはずです。古典的な継承は、他のクラスから継承するクラスに限定されます。ただし、プロトタイプの継承には、他のプロトタイプから継承するプロトタイプだけでなく、プロトタイプから継承するオブジェクトも含まれます。
プロトタイプクラスの同型
プロトタイプとクラスは非常に似ていることに気づいたはずです。それは本当だ。彼らです。実際、それらは非常に似ているので、実際にプロトタイプを使用してクラスをモデル化できます。
function CLASS(base, body) {
if (arguments.length < 2) body = base, base = Object.prototype;
var prototype = Object.create(base, {new: {value: create}});
return body.call(prototype, base), prototype;
function create() {
var self = Object.create(prototype);
return prototype.hasOwnProperty("constructor") &&
prototype.constructor.apply(self, arguments), self;
}
}
上記のCLASS
関数を使用して、クラスのようなプロトタイプを作成できます。
var Human = CLASS(function () {
var milliseconds = 1
, seconds = 1000 * milliseconds
, minutes = 60 * seconds
, hours = 60 * minutes
, days = 24 * hours
, years = 365.2425 * days;
this.constructor = function (name, sex, dob) {
this.name = name;
this.sex = sex;
this.dob = dob;
};
this.age = function () {
return Math.floor((new Date - this.dob) / years);
};
});
var Man = CLASS(Human, function (Human) {
this.constructor = function (name, dob) {
Human.constructor.call(this, name, "male", dob);
if (this.age() < 18) throw new Error(name + " is a boy, not a man!");
};
});
var johnDoe = Man.new("John Doe", new Date(1970, 0, 1));
ただし、その逆は当てはまりません(つまり、クラスを使用してプロトタイプをモデル化することはできません)。これは、プロトタイプはオブジェクトですが、クラスはオブジェクトではないためです。それらは完全に異なるタイプの抽象化です。
結論
要約すると、抽象化は「特定の例から共通の特徴を抽出することによって形成される一般的な概念」であり、一般化は「より具体的な抽象化の抽象化」であることがわかりました。また、プロトタイプ継承とクラシック継承の違い、および両方が同じコインの2つの面である方法についても学びました。
パーティングノートで、プロトタイプの継承には、プロトタイプのパターンとコンストラクターのパターンという2つのパターンがあることに注意したいと思います。原型パターンは原型継承の標準パターンであり、コンストラクタパターンは原型継承を古典的な継承のようにするために使用されます。個人的には私はプロトタイプのパターンを好みます。
PS私は、ブログ投稿「プロトタイプの継承が重要である理由」を書いて、「クラシックに対するプロトタイプの継承の利点?」という質問に答えた人です。私の答えは受け入れられた答えです。