Object.create
以下では、継承を設定するのに が望ましい理由だけに関心があると仮定します。
その利点を理解するために、まず JavaScript で「クラス」が何で構成されているかを明確にしましょう。次の 2 つの部分があります。
コンストラクター関数。この関数には、「クラス」のインスタンス、つまりインスタンス固有のコードを作成するためのすべてのロジックが含まれています。
プロトタイプオブジェクト。これは、インスタンスが継承するオブジェクトです。これには、すべてのインスタンス間で共有する必要があるすべてのメソッド (およびその他のプロパティ) が含まれています。
継承は is-a関係を確立します。たとえば、 aDog
はanAnimal
です。これは、コンストラクター関数とプロトタイプ オブジェクトの観点からどのように表現されますか?
明らかに、犬は動物と同じメソッドを持っている必要があります。つまり、Dog
プロトタイプオブジェクトは、何らかの方法でAnimal
プロトタイプオブジェクトのメソッドを組み込む必要があります。これを行うには複数の方法があります。あなたはしばしばこれを見るでしょう:
Dog.prototype = new Animal();
これが機能するのは、Animal
インスタンスがAnimal
プロトタイプオブジェクトから継承するためです。しかし、それはまた、すべての犬が1 つの特定のAnimal
インスタンスから継承することも意味します。それは少し奇妙に思えます。インスタンス固有のコードは、コンストラクター関数でのみ実行するべきではありませんか? 突然、インスタンス固有のコードとプロトタイプメソッドが混在しているように見えます。
Animal
その時点でインスタンス固有のコードを実際に実行する必要はありません。必要なのは、Animal
プロトタイプオブジェクトのすべてのメソッドだけです。それがObject.create
私たちができることです:
Dog.prototype = Object.create(Animal.prototype);
ここでは、新しいインスタンスを作成していませんAnimal
。プロトタイプ メソッドのみを取得します。インスタンス固有のコードは、コンストラクター内の本来あるべき場所で正確に実行されます。
function Dog() {
Animal.call(this, 'Dog');
}
最大の利点は、常にObject.create
機能することです。Usingは、コンストラクターが引数を想定していない場合にのみ機能します。コンストラクターが次のようになっていると想像してください。new Animal()
function Animal(name) {
this.name = name.toLowerCase();
}
には常に文字列を渡すAnimal
必要があります。そうしないと、エラーが発生します。あなたがするとき、あなたは何を渡しますDog.prototype = new Animal(??);
か?何かを渡す限り、実際にはどの文字列を渡すかは問題ではありません。これは、これが悪い設計であることを示していることを願っています。
Dog.prototype = Animal.prototype;
それもうまくいくと言う人もいます。だから今、私は完全に混乱しています
Animal.prototype
プロパティを からに「追加」するものはすべて「機能」しますDog.prototype
。しかし、ソリューションの品質は異なります。この場合、 に追加するメソッドが にDog.prototype
も追加されるという問題が発生しますAnimal.prototype
。
例:
Dog.prototype.bark = function() {
alert('bark');
};
以来Dog.prototype === Animal.prototype
、すべてのAnimal
インスタンスがメソッドを持つようbark
になりましたが、これは確かにあなたが望むものではありません。
Object.create
(さらにはnew Animal
)から継承する新しいオブジェクトを作成することにより、継承に1レベルの間接性を追加しAnimal.prototype
、その新しいオブジェクトは になりDog.prototype
ます。
ES6 での継承
ES6 では、コンストラクター関数とプロトタイプ メソッドを作成するための新しい構文が導入されています。これは次のようになります。
class Dog extends Animal {
bark() {
alert('bark');
}
}
これは上で説明したものよりも便利ですが、結局のところ、継承をセットアップするためにextends
内部で同等のものも使用しています。ES6 ドラフトObject.create
の手順 2 と 3 を参照してください。
つまり、ES5 では using が「より正しい」アプローチです。
Object.create(SuperClass.prototype)