いいえ、いいえ、いいえ、これはできません。JavaScript での継承はすべて間違っています。あなたのコードは私に片頭痛を与えます。
JavaScript で疑似古典的な継承パターンを作成する
JavaScript のクラスに似たものが必要な場合は、それを提供するライブラリがたくさんあります。たとえば、augmentを使用すると、次のようにコードを再構築できます。
var augment = require("augment");
var ABC = augment(Object, function () {
this.constructor = function (key, value) {
this.key = key;
this.value = value;
};
this.what = function () {
alert("what");
};
});
var XYZ = augment(ABC, function (base) {
this.constructor = function (key, value) {
base.constructor.call(this, key, value);
};
this.that = function () {
alert("that");
};
});
あなたのことはわかりませんが、私にはこれは C++ や Java の古典的な継承によく似ています。これで問題が解決した場合は、すばらしいです。そうでない場合は、読み続けてください。
プロトタイプクラスの同形
多くの点で、プロトタイプはクラスに似ています。実際、プロトタイプとクラスは非常に似ているため、プロトタイプを使用してクラスをモデル化できます。まず、プロトタイプの継承が実際にどのように機能するかを見てみましょう。

上の写真は、次の回答から取得したものです。注意深く読むことをお勧めします。図は次のことを示しています。
prototype
すべてのコンストラクターには、コンストラクター関数のプロトタイプ オブジェクトを指すというプロパティがあります。
- すべてのプロトタイプには
constructor
、プロトタイプ オブジェクトのコンストラクター関数を指す と呼ばれるプロパティがあります。
- コンストラクター関数からインスタンスを作成します。ただし、インスタンスは実際に
prototype
はコンストラクターではなく から継承します。
これは非常に有益な情報です。従来、コンストラクター関数を最初に作成してから、そのprototype
プロパティを設定していました。constructor
ただし、この情報は、最初にプロトタイプ オブジェクトを作成してから、代わりにそのプロパティを定義できることを示しています。
たとえば、伝統的に次のように書くことができます。
function ABC(key, value) {
this.key = key;
this.value = value;
}
ABC.prototype.what = function() {
alert("what");
};
ただし、新たに発見した知識を使用すると、次のように同じことを書くことができます。
var ABC = CLASS({
constructor: function (key, value) {
this.key = key;
this.value = value;
},
what: function () {
alert("what");
}
});
function CLASS(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
ご覧のとおり、カプセル化は JavaScript で簡単に実現できます。あなたがする必要があるのは、横向きに考えるだけです。ただし、継承は別の問題です。継承を実現するには、もう少し作業を行う必要があります。
継承とスーパー
augment
継承を実現する方法を見てみましょう:
function augment(body) {
var base = typeof this === "function" ? this.prototype : this;
var prototype = Object.create(base);
body.apply(prototype, arrayFrom(arguments, 1).concat(base));
if (!ownPropertyOf(prototype, "constructor")) return prototype;
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
CLASS
最後の 3 行は、前のセクションのものと同じであることに注意してください。
function CLASS(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
これは、プロトタイプ オブジェクトを取得したら、そのコンストラクタ プロパティを取得して返すだけでよいことを示しています。
拡張の最初の 3 行は、次の目的で使用されます。
- 基本クラスのプロトタイプを取得します。
- を使用して派生クラスのプロトタイプを作成します
Object.create
。
- 派生クラスのプロトタイプに、指定されたプロパティを設定します。
JavaScript の継承については、これですべてです。独自の古典的な継承パターンを作成したい場合は、同じ方向に沿って考える必要があります。
真のプロトタイプ継承を受け入れる
熟練した JavaScript プログラマーなら誰でも、プロトタイプの継承は従来の継承よりも優れていると言うでしょう。それにもかかわらず、古典的な継承を持つ言語から来た初心者は、常にプロトタイプの継承の上に古典的な継承を実装しようとしますが、通常は失敗します。
それらが失敗するのは、プロトタイプの継承の上に古典的な継承を実装することができないからではなく、プロトタイプの継承の上に古典的な継承を実装するには、最初に真のプロトタイプの継承がどのように機能するかを理解する必要があるためです。
ただし、真のプロトタイプ継承を理解すると、古典的な継承には戻りたくないでしょう。私も初心者として、プロトタイプの継承の上に古典的な継承を実装しようとしました。真のプロトタイプ継承がどのように機能するかを理解したので、次のようなコードを記述します。
function extend(self, body) {
var base = typeof self === "function" ? self.prototype : self;
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;
}
}
上記のextend
関数は と非常によく似ていaugment
ます。ただし、コンストラクター関数を返す代わりに、プロトタイプ オブジェクトを返します。これは実際には、静的プロパティを継承できる非常に巧妙なトリックです。extend
次のように使用してクラスを作成できます。
var Abc = extend(Object, function () {
this.constructor = function (key, value) {
this.value = 333 + Number(value);
this.key = key;
};
this.what = function () {
alert("what");
};
});
継承も同様に単純です。
var Xyz = extend(Abc, function (base) {
this.constructor = function (key, value) {
base.constructor.call(this, key, value);
};
this.that = function () {
alert("that");
};
});
ただしextend
、コンストラクター関数を返さないことに注意してください。プロトタイプ オブジェクトを返します。new
これは、キーワードを使用してクラスのインスタンスを作成できないことを意味します。new
代わりに、次のようにメソッドとして使用する必要があります。
var x = Xyz.new("x", "123");
var y = Xyz.new("y", "456");
var it = Abc.new("it", "789");
これは実際には良いことです。このnew
キーワードは有害であると考えられ ているため、使用を中止することを強くお勧めします。たとえば、キーワードと一緒に使用することはできません。ただし、次の方法で使用することは可能です。apply
new
apply
new
var it = Abc.new.apply(null, ["it", "789"]);
Abc
andXyz
はコンストラクター関数ではないため、オブジェクトが or のインスタンスであるかどうかをテストするために使用するinstanceof
ことAbc
はできませんXyz
。ただし、JavaScript にはisPrototypeOf
、オブジェクトが別のオブジェクトのプロトタイプであるかどうかをテストするメソッドが呼び出されるため、これは問題ではありません。
alert(x.key + ": " + x.value + "; isAbc: " + Abc.isPrototypeOf(x));
alert(y.key + ": " + y.value + "; isAbc: " + Abc.isPrototypeOf(y));
alert(it.key + ": " + it.value + "; isAbc: " + Abc.isPrototypeOf(it));
alert(it.key + ": " + it.value + "; isXyz: " + Xyz.isPrototypeOf(it));
実際には、あるクラスが別のクラスを拡張するかどうかをテストできるため、isPrototypeOf
より強力です。instanceof
alert(Abc.isPrototypeOf(Xyz)); // true
この小さな変更を除けば、他のすべては以前と同じように機能します。
x.what();
y.that();
it.what();
it.that(); // will throw; it is not Xyz and does not have that method
デモをご覧ください: http://jsfiddle.net/Jee96/
真のプロトタイプ継承は他に何を提供しますか? 真のプロトタイプ継承の最大の利点の 1 つは、通常のプロパティと静的プロパティの間に区別がなく、次のようなコードを記述できることです。
var Xyz = extend(Abc, function (base) {
this.empty = this.new();
this.constructor = function (key, value) {
base.constructor.call(this, key, value);
};
this.that = function () {
alert("that");
};
});
を呼び出すことにより、クラス自体からクラスのインスタンスを作成できることに注意してくださいthis.new
。がまだ定義されていない場合this.constructor
は、初期化されていない新しいインスタンスを返します。それ以外の場合は、新しい初期化されたインスタンスを返します。
さらに、は直接Xyz
アクセスできるプロトタイプ オブジェクトです(つまり、 は の静的プロパティです)。これは、静的プロパティが自動的に継承され、通常のプロパティと変わらないことも意味します。Xyz.empty
empty
Xyz
最後に、クラスは としてクラス定義内からアクセスできるため、次のようthis
に使用して、ネストされているクラスから継承するネストされたクラスを作成できます。extend
var ClassA = extend(Object, function () {
var ClassB = extend(this, function () {
// class definition
});
// rest of the class definition
alert(this.isPrototypeOf(ClassB)); // true
});
デモをご覧ください: http://jsfiddle.net/Jee96/1/