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 Person
true を返し、Student
デリゲートのインスタンスがに正しく渡されるようにStudent.prototype
、デリゲートを結び付ける必要がありPerson.prototype
ます。
しかし、それをnew
キーワードで呼び出しているので、その呼び出しが何を追加するか覚えていますか?それは呼ぶだろうObject.create(Student.prototype)
、我々は間のdelegational関係を設定する方法である、Student
とStudent.prototype
。現在、Student.prototype
空であることに注意してください。したがって、にのみ委任されているため.species
、のインスタンスのStudent
検索は失敗し、プロパティはに存在しません。 Student.prototype
.species
Student.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;
}
}