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теперь встроен способ, которым мы пишем классы - например, он предоставляется как метод, когда мы объявляем класс. Это просто синтаксический сахар, но он предоставляет нам некоторые удобства, такие как доступ к 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;
}
}