プロトタイプチェーンへようこそ!
あなたの例でそれがどのように見えるか見てみましょう。
問題
を呼び出すと、配列を参照するnew Thing()
プロパティを持つ新しいオブジェクトが作成されます。relatedThings
したがって、次のように言えます。
+--------------+
|Thing instance|
| |
| relatedThings|----> Array
+--------------+
次に、このインスタンスを に割り当てますThingA.prototype
:
+--------------+
| ThingA | +--------------+
| | |Thing instance|
| prototype |----> | |
+--------------+ | relatedThings|----> Array
+--------------+
したがって、の各インスタンスはインスタンスThingA
から継承されThing
ます。ここで、新しいインスタンスを作成して各プロトタイプに割り当て、後で and のインスタンスを作成しますThingA1
( andと、ただしここには示されていません)。ThingA2
ThingA
ThingA1
ThingA2
ThingA
Thing
関係は次のようになりました (__proto__
は内部プロパティであり、オブジェクトとそのプロトタイプを接続します):
+-------------+
| ThingA |
| |
+-------------+ | prototype |----+
| ThingA1 | +-------------+ |
| | |
| prototype |---> +--------------+ |
+-------------+ | ThingA | |
| instance (1) | |
| | |
+-------------+ | __proto__ |--------------+
| ThingA1 | +--------------+ |
| instance | ^ |
| | | v
| __proto__ |-----------+ +--------------+
+-------------+ |Thing instance|
| |
| relatedThings|---> Array
+-------------+ +--------------+ +--------------+
| ThingA2 | | ThingA | ^
| | | instance (2) | |
| prototype |---> | | |
+-------------+ | __proto__ |--------------+
+--------------+
+-------------+ ^
| ThingA2 | |
| instance | |
| | |
| __proto__ |-----------+
+-------------+
ThingA
そのため、ThingA1
またはのすべてのインスタンスは、 ThingA2
1 つの同じ配列インスタンスを参照します。
これはあなたが望むものではありません!
ソリューション
この問題を解決するには、「サブクラス」の各インスタンスに独自の relatedThings
プロパティが必要です。super()
これは、他の言語での呼び出しと同様に、各子コンストラクターで親コンストラクターを呼び出すことで実現できます。
function ThingA() {
Thing.call(this);
}
function ThingA1() {
ThingA.call(this);
}
// ...
これは、これらの関数内で and を呼び出しThing
、に渡す最初の引数にThingA
設定します。[MDN]と[MDN ]の詳細をご覧ください。this
.call
.call
this
これだけで、上の図が次のように変わります。
+-------------+
| ThingA |
| |
+-------------+ | prototype |----+
| ThingA1 | +-------------+ |
| | |
| prototype |---> +--------------+ |
+-------------+ | ThingA | |
| instance (1) | |
| | |
| relatedThings|---> Array |
+-------------+ | __proto__ |--------------+
| ThingA1 | +--------------+ |
| instance | ^ |
| | | |
|relatedThings|---> Array | v
| __proto__ |-----------+ +--------------+
+-------------+ |Thing instance|
| |
| relatedThings|---> Array
+-------------+ +--------------+ +--------------+
| ThingA2 | | ThingA | ^
| | | instance (2) | |
| prototype |---> | | |
+-------------+ | relatedThings|---> Array |
| __proto__ |--------------+
+--------------+
+-------------+ ^
| ThingA2 | |
| instance | |
| | |
|relatedThings|---> Array |
| __proto__ |-----------+
+-------------+
ご覧のとおり、各インスタンスには独自のrelatedThings
プロパティがあり、異なる配列インスタンスを参照しています。プロトタイプ チェーンにはまだrelatedThings
プロパティがありますが、それらはすべてインスタンス プロパティによって隠されています。
より良い継承
また、プロトタイプを次のように設定しないでください。
ThingA.prototype = new Thing();
Thing
実際には、ここで新しいインスタンスを作成する必要はありません。Thing
予想される引数の場合はどうなりますか? あなたはどちらに合格しますか?コンストラクターの呼び出しThing
に副作用がある場合はどうなりますか?
実際に必要なのはThing.prototype
、プロトタイプ チェーンに接続することです。Object.create
[MDN]でこれを行うことができます:
ThingA.prototype = Object.create(Thing.prototype);
コンストラクター ( Thing
) が実行されたときに発生することはすべて、後で実際に新しいインスタンスを作成するときに発生します (上記のようThingA
に呼び出すことによって)。Thing.call(this)