В разделе о наследовании в статье MDN Введение в объектно-ориентированный Javascript я заметил, что они установили prototype.constructor:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
Служит ли это какой-либо важной цели? Можно ли это пропустить?
javascript
oop
inheritance
trinth
источник
источник
subclass.prototype.constructor
Будет указывать ,parent_class
если не писатьsubclass.prototype.constructor = subclass
; То есть использованиеsubclass.prototype.constructor()
напрямую даст неожиданный результат.Ответы:
Это не всегда необходимо, но у него есть свое применение. Предположим, мы хотели создать метод копирования для базового
Person
класса. Как это:Что происходит, когда мы создаем новое
Student
и копируем его?Копия не является экземпляром
Student
. Это потому, что (без явных проверок) у нас не было бы возможности вернутьStudent
копию из «базового» класса. Мы можем только вернутьPerson
. Однако, если мы сбросили конструктор:... тогда все работает как положено:
источник
constructor
Атрибут не имеет никакого особого значения в JS, так что вы можете также назвать егоbananashake
. Единственное отличие состоит в том, что движок автоматически инициализируетсяconstructor
приf.prototype
каждом объявлении функцииf
. Тем не менее, он может быть перезаписан в любое время.constructor
означает , что она делает имеют особое значение в JS, в значительной степени по определению.return new this.constructor(this.name);
вместоreturn new Person(this.name);
. Посколькуthis.constructor
этоStudent
функция (потому что вы установили ее с помощьюStudent.prototype.constructor = Student;
),copy
функция в конечном итоге вызываетStudent
функцию. Я не уверен, что вы хотели сделать с//just as bad
комментарием.Student
конструктор добавил дополнительный аргумент вродеStudent(name, id)
:? Должны ли мы затем переопределитьcopy
функцию, вызвавPerson
версию из нее, а затем скопировав дополнительноеid
свойство?Да и нет.
В ES5 и более ранних версиях сам JavaScript ни
constructor
для чего не использовался . Он определил, что объект по умолчанию вprototype
свойстве функции будет иметь его и будет ссылаться на функцию, и на этом все . Ничто другое в спецификации не ссылается на это вообще.Это изменилось в ES2015 (ES6), который начал использовать его в отношении иерархий наследования. Например, при создании нового обещания для возврата
Promise#then
используетсяconstructor
свойство обещания, для которого вы его вызываете (через SpeciesConstructor ). Он также участвует в подтипировании массивов (через ArraySpeciesCreate ).Вне самого языка иногда люди использовали его при попытке создания общих функций «клонирования» или просто в целом, когда они хотели ссылаться на то, что, по их мнению, было бы функцией конструктора объекта. Мой опыт показывает, что использовать его редко, но иногда люди используют его.
Он там по умолчанию, вам нужно только вернуть его, когда вы замените объект в
prototype
свойстве функции :Если вы этого не сделаете:
... то
Student.prototype.constructor
наследует отPerson.prototype
которого (предположительно) имеетconstructor = Person
. Так что это вводит в заблуждение. И, конечно, если вы создаете подклассы для чего-то, что использует это (например,Promise
илиArray
) и не используетеclass
¹ (который обрабатывает это для вас), вы захотите убедиться, что вы установили это правильно. Итак, в основном: это хорошая идея.Это нормально, если ничто в вашем коде (или коде библиотеки, который вы используете) не использует его. Я всегда следил, чтобы он был правильно подключен.
Конечно, с ключевым словом ES2015 (он же ES6)
class
большую часть времени мы бы его использовали, нам больше не нужно, потому что оно обрабатывается для нас, когда мы делаем¹ «... если вы подклассифицируете что-то, что использует это (например,
Promise
илиArray
), и не используетеclass
...» - это возможно сделать, но это настоящая боль (и немного глупая). Вы должны использоватьReflect.construct
.источник
TLDR; Не супер необходим, но, вероятно, поможет в долгосрочной перспективе, и это более точно.
ПРИМЕЧАНИЕ: многое отредактировано, так как мой предыдущий ответ был сбивчиво написан, и в нем было несколько ошибок, которые я упустил, пытаясь ответить. Спасибо тем, кто указал на некоторые вопиющие ошибки.
По сути, это правильно подключить подклассы в Javascript. Когда мы создаем подкласс, мы должны сделать несколько забавных вещей, чтобы убедиться, что прототипное делегирование работает правильно, включая перезапись
prototype
объекта. Перезаписьprototype
объекта включает в себяconstructor
, поэтому нам нужно исправить ссылку.Давайте быстро рассмотрим, как работают «классы» в ES5.
Допустим, у вас есть функция конструктора и ее прототип:
Когда вы вызываете конструктор для создания экземпляра, говорите
Adam
:new
Ключевое слово вызывается с «Персона» в основном будет работать конструктор Person с несколькими дополнительными строками кода:Если мы
console.log(adam.species)
, то поиск будет терпеть неудачу вadam
случае, и посмотреть вверх прототипичную цепь к его.prototype
, котораяPerson.prototype
- иPerson.prototype
имеет в.species
собственность, так что поиск будет успешным вPerson.prototype
. Это тогда войдет'human'
.Здесь
Person.prototype.constructor
правильно будет указыватьPerson
.Итак, теперь интересная часть, так называемый «подкласс». Если мы хотим создать
Student
класс, который является подклассомPerson
класса с некоторыми дополнительными изменениями, нам нужно убедиться, что онStudent.prototype.constructor
указывает на точность ученика.Это не делает это само по себе. Когда вы создаете подкласс, код выглядит так:
Вызов
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. Таким образом, мы должны исправить все это.Сейчас делегация работает, но мы перезаписываем
Student.prototype
с офPerson.prototype
. Так что, если мы позвонимStudent.prototype.constructor
, это будет указыватьPerson
вместоStudent
. Вот почему мы должны это исправить.В ES5 наше
constructor
свойство является ссылкой, которая ссылается на функцию, которую мы написали с намерением стать «конструктором». Помимо того, чтоnew
дает нам ключевое слово, конструктор в противном случае является «простой» функцией.В ES6
constructor
теперь встроен способ, которым мы пишем классы - например, он предоставляется как метод, когда мы объявляем класс. Это просто синтаксический сахар, но он предоставляет нам некоторые удобства, такие как доступ кsuper
классу, когда мы расширяем существующий класс. Поэтому мы бы написали приведенный выше код так:источник
eve instanceof Student
вернулсяtrue
. См. Stackoverflow.com/questions/35537995/… для объяснения. Кроме того, когда вы говорите,which is, at the moment, nothing
что вы имеете в виду? У каждой функции есть прототип, поэтому, если я проверю,Student.prototype
это что-то.Object.create(Person.prototype)
,Student.prototype
пусто. Поэтому, если мы войдем в системуeve.species
, он не будет делегировать должным образом до своего суперкласса Person, и он не будет регистрироваться'human'
. Предположительно, мы хотим, чтобы каждый подкласс наследовал от своего прототипа, а также прототипа своего супер-класса.which is, at the moment, nothing
я имел в виду, чтоStudent.prototype
объект пуст.Student.prototype
toObject.create(Person.prototype)
- что, если вы помните, одинаково, как все экземпляры Person настроены на делегированиеPerson.prototype
- поиск свойства на экземпляреStudent
делегировал бы толькоStudent.prototype
. Такeve.species
что провалит его поиск. Если мы назначим егоStudent.prototype
сам, тогда делегатPerson.prototype
, и поискeve.species
вернетсяhuman
.instance
« подкласса », он будет точным». Нет,instanceof
не используетconstructor
. «Однако, если мы посмотрим на студенческий .prototype.constructor, он все равно будет указывать на Персона» Нет, так и будетStudent
. Я не понимаю смысл этого примера. Вызов функции в конструкторе не является наследованием. «В ES6 конструктор теперь является реальной функцией, а не ссылкой на функцию». Что?Я бы не согласился. Нет необходимости устанавливать прототип. Возьмите тот же самый код, но удалите строку prototype.constructor. Что-нибудь меняется? Нет. Теперь внесите следующие изменения:
и в конце кода теста ...
Цвет будет синим.
По моему опыту, изменение в prototype.constructor мало что даст, если вы не делаете очень конкретные, очень сложные вещи, которые, вероятно, в любом случае не являются хорошей практикой :)
Редактировать: После того, как вы покопались в сети и немного поэкспериментировали, похоже, что люди настраивают конструктор так, чтобы он «выглядел» как то, что создается с помощью «new». Думаю, я бы сказал, что проблема в том, что javascript является языком-прототипом - не существует такой вещи, как наследование. Но большинство программистов происходят из опыта программирования, который выдвигает наследование как «путь». Таким образом, мы придумываем все виды вещей, чтобы попытаться сделать этот язык-прототип «классическим» языком… например, расширение «классов». Действительно, в приведенном ими примере новый ученик - это человек - он не «расширяется» от другого ученика… ученик - это все о человеке, и кем бы ни был этот ученик. Расширьте ученика, и все, что вы
Крокфорд немного сумасшедший и излишне усердный, но серьезно почитайте кое-что из того, что он написал ... это заставит вас взглянуть на это совсем по-другому.
источник
Это огромная ловушка, если вы написали
но тогда, если был Учитель, чей прототип был также Человек, и вы написали
тогда ученик конструктор теперь учитель!
Изменить: вы можете избежать этого, убедившись, что вы установили прототипы ученика и учителя, используя новые экземпляры класса Person, созданные с использованием Object.create, как в примере Mozilla.
источник
Student.prototype = Object.create(...)
предполагается в этом вопросе. Этот ответ добавляет только возможную путаницу.Object.create(...)
используется в статье MDN, которая породила вопрос, но не в самом вопросе. Я уверен, что многие люди не переходят.До сих пор путаница все еще там.
Следуя исходному примеру, поскольку у вас есть существующий объект
student1
как:Предположим, вы не хотите знать, как
student1
создается, вы просто хотите другой объект, подобный этому, вы можете использовать свойство constructorstudent1
вроде:Здесь не удастся получить свойства,
Student
если свойство конструктора не установлено. Скорее это создастPerson
объект.источник
Получил хороший пример кода того, почему действительно необходимо установить конструктор прототипа.
источник
createNewCar
метод создает фабрики !? Также это выглядит так, как будто это следовало использоватьvar audiFactory = new CarFactory("Audi")
вместо наследования.this.constructor
внутренне, поэтому не удивительно, что его нужно установить. У вас есть пример без него?В наши дни не нужно использовать классы с сахаром или использовать «New». Используйте объектные литералы.
Прототип Object уже является «классом». Когда вы определяете литерал объекта, это уже экземпляр прототипа Object. Они также могут выступать в качестве прототипа другого объекта и т. Д.
Это стоит прочитать :
источник
Это необходимо, когда вам нужна альтернатива
toString
без обезьяноподготовки:источник
foo.constructor()
??РЕДАКТИРОВАТЬ, я был на самом деле не так. Комментирование строки не меняет ее поведение вообще. (Я проверял это)
Да, это необходимо. Когда вы делаете
Student.prototype.constructor
становитсяPerson
. Следовательно, вызовStudent()
будет возвращать объект, созданныйPerson
. Если вы тогда делаетеStudent.prototype.constructor
сбрасывается обратно вStudent
. Теперь, когда вы вызываетеStudent()
егоStudent
, он выполняет , который вызывает родительский конструкторParent()
, он возвращает правильно унаследованный объект. Если вы не произвели сбросStudent.prototype.constructor
до вызова, вы получите объект, для которого не было бы установлено ни одно из свойствStudent()
.источник
Дана простая функция конструктора:
По умолчанию (из спецификации https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ) все прототипы автоматически получают свойство с именем constructor, которое указывает на функцию на который является собственностью. В зависимости от конструктора, к прототипу могут быть добавлены другие свойства и методы, что не является обычной практикой, но все же допускается для расширений.
Итак, просто отвечая: нам нужно убедиться, что значение в prototype.constructor установлено правильно, как предполагается в спецификации.
Должны ли мы всегда правильно устанавливать это значение? Это помогает при отладке и делает внутреннюю структуру согласованной со спецификацией. Мы должны определенно, когда наш API используется третьими сторонами, но не совсем, когда код наконец выполняется во время выполнения.
источник
Вот один пример из MDN, который я нашел очень полезным для понимания его использования.
В JavaScript у нас есть,
async functions
который возвращает объект AsyncFunction .AsyncFunction
не является глобальным объектом, но его можно получить с помощьюconstructor
свойства и использовать его.источник
Это не обязательно. Чемпионы ООП стараются превратить прототипическое наследование JavaScript в классическое наследование - это лишь одна из многих традиционных вещей. Единственное что следующее
делает, что теперь у вас есть ссылка на текущий "конструктор".
В ответе Уэйна, который был помечен как правильный, вы можете сделать то же самое, что и следующий код
с кодом ниже (просто замените this.constructor на Person)
Слава Богу, что с классическим наследованием ES6 пуристы могут использовать нативные операторы языка, такие как class, extends и super, и нам не нужно видеть подобные как prototype.constructor исправления и родительские ссылки.
источник