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
Ключове слово викликається з «Персона» в основному буде працювати конструктор Person з декількома додатковими рядками коду:
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
вказує Студент на точність.
Це не робить це само собою. Коли ви підклас, код виглядає так:
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
повертав істину і таким чином, щоб екземпляри Student
делегували правильно до Student.prototype
, а потім Person.prototype
.
Але, оскільки ми називаємо це new
ключовим словом, пам’ятайте, що додає ця виклик? Це закликає Object.create(Student.prototype)
, саме так ми встановлювали відносини делегації між 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
тепер вбудований у спосіб, коли ми пишемо класи - як у, він надається як метод, коли ми оголошуємо клас. Це просто синтаксичний цукор, але він надає нам деякі зручності, такі як доступ до a, super
коли ми розширюємо існуючий клас. Отже, ми б написали наведений вище код таким чином:
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;
}
}