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;
}
}