TLDR; それほど必要ではありませんが、おそらく長期的には役立つでしょう。そうする方がより正確です。
注:私の以前の回答が混乱して書かれていて、急いで回答しなかったいくつかのエラーがあったため、多くの編集が行われました。いくつかの重大なエラーを指摘した人々に感謝します。
基本的には、Javascriptでサブクラス化を正しく配線することです。サブクラスを作成するときは、prototypeオブジェクトの上書きなど、プロトタイプの委任が正しく機能することを確認するために、いくつかファンキーなことを行う必要があります。prototypeオブジェクトの上書きには、constructorため、参照を修正する必要があります。
ES5の「クラス」の仕組みを簡単に見ていきましょう。
コンストラクタ関数とそのプロトタイプがあるとしましょう:
//Constructor Function
var Person = function(name, age) {
this.name = name;
this.age = age;
}
//Prototype Object - shared between all instances of Person
Person.prototype = {
species: 'human',
}
インスタンス化するコンストラクタを呼び出すときは、次のように言いますAdam。
// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);
new「人」で呼び出されたキーワードは、基本的には、コードのいくつかの追加のラインを持つ人のコンストラクタを実行します:
function Person (name, age) {
// This additional line is automatically added by the keyword 'new'
// it sets up the relationship between the instance and the prototype object
// So that the instance will delegate to the Prototype object
this = Object.create(Person.prototype);
this.name = name;
this.age = age;
return this;
}
/* So 'adam' will be an object that looks like this:
* {
* name: 'Adam',
* age: 19
* }
*/
私たちのconsole.log(adam.species)場合、ルックアップはadamインスタンスで失敗し、プロトタイプチェーンをその.prototypeまでルックアップします。つまり、プロパティPerson.prototypeをPerson.prototype 持っている.speciesため、ルックアップはで成功しPerson.prototypeます。次にログに記録します'human'。
ここでは、Person.prototype.constructorは正しくを指しPersonます。
ここで興味深いのは、いわゆる「サブクラス化」です。Studentクラスを作成する場合、それはクラスのサブクラスでありPerson、いくつかの追加の変更が加えられているため、次のことを確認する必要があります。Student.prototype.constructor、正確を期すためにStudentを指すます。
それ自体ではこれを行いません。サブクラス化すると、コードは次のようになります。
var Student = function(name, age, school) {
// Calls the 'super' class, as every student is an instance of a Person
Person.call(this, name, age);
// This is what makes the Student instances different
this.school = school
}
var eve = new Student('Eve', 20, 'UCSF');
console.log(Student.prototype); // this will be an empty object: {}
new Student()ここで呼び出すと、必要なすべてのプロパティを持つオブジェクトが返されます。ここで、をチェックするとeve instanceof Person、が返されfalseます。にアクセスしようとするとeve.species、戻りますundefinedます。
つまり、eve instanceof Persontrue を返し、Studentデリゲートのインスタンスがに正しく渡されるようにStudent.prototype、デリゲートを結び付ける必要がありPerson.prototypeます。
しかし、それをnewキーワードで呼び出しているので、その呼び出しが何を追加するか覚えていますか?それは呼ぶだろうObject.create(Student.prototype)、我々は間のdelegational関係を設定する方法である、StudentとStudent.prototype。現在、Student.prototype空であることに注意してください。したがって、にのみ委任されているため.species、のインスタンスのStudent検索は失敗し、プロパティはに存在しません。 Student.prototype.speciesStudent.prototypeます。
に代入Student.prototypeするとObject.create(Person.prototype)、Student.prototypeそれ自体がに委譲されPerson.prototype、期待どおりにルックアップeve.speciesが戻りhumanます。おそらく、Student.prototype AND Person.prototypeから継承する必要があります。したがって、すべてを修正する必要があります。
/* This sets up the prototypal delegation correctly
*so that if a lookup fails on Student.prototype, it would delegate to Person's .prototype
*This also allows us to add more things to Student.prototype
*that Person.prototype may not have
*So now a failed lookup on an instance of Student
*will first look at Student.prototype,
*and failing that, go to Person.prototype (and failing /that/, where do we think it'll go?)
*/
Student.prototype = Object.create(Person.prototype);
これで委任は機能しますが、ので上書きStudent.prototypeしていますPerson.prototype。したがって、を呼び出すとStudent.prototype.constructor、Personではなくを指しStudentます。これが、修正する必要がある理由です。
// Now we fix what the .constructor property is pointing to
Student.prototype.constructor = Student
// If we check instanceof here
console.log(eve instanceof Person) // true
ES5では、constructorプロパティは、「コンストラクター」になることを意図して記述した関数を参照する参照です。何を除いてnewキーワードが私たちを与える、コンストラクタは、そうでない場合は「プレーン」関数です。
ES6では、constructorクラスの記述方法にが組み込まれました。これは、クラスを宣言するときのメソッドとして提供されます。これは単に構文上の砂糖ですがsuper、既存のクラスを拡張するときのaへのアクセスなど、いくつかの便利さを備えています。したがって、上記のコードを次のように記述します。
class Person {
// constructor function here
constructor(name, age) {
this.name = name;
this.age = age;
}
// static getter instead of a static property
static get species() {
return 'human';
}
}
class Student extends Person {
constructor(name, age, school) {
// calling the superclass constructor
super(name, age);
this.school = school;
}
}