Im Abschnitt über die Vererbung im MDN-Artikel Einführung in objektorientiertes Javascript habe ich festgestellt, dass der Prototyp.constructor festgelegt wurde:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
Dient dies einem wichtigen Zweck? Ist es in Ordnung, es wegzulassen?
javascript
oop
inheritance
dreizehnten
quelle
quelle
subclass.prototype.constructor
wird darauf hinweisen,parent_class
wenn Sie nicht schreibensubclass.prototype.constructor = subclass
; Das heißt, diesubclass.prototype.constructor()
direkte Verwendung führt zu einem unerwarteten Ergebnis.Antworten:
Es ist nicht immer notwendig, aber es hat seine Verwendung. Angenommen, wir wollten eine Kopiermethode für die Basisklasse
Person
erstellen. So was:Was passiert nun, wenn wir ein neues erstellen
Student
und es kopieren?Die Kopie ist keine Instanz von
Student
. Dies liegt daran, dass wir (ohne explizite Überprüfungen) keine Möglichkeit hätten, eineStudent
Kopie aus der "Basis" -Klasse zurückzugeben. Wir können nur a zurückgebenPerson
. Wenn wir jedoch den Konstruktor zurückgesetzt hätten:... dann funktioniert alles wie erwartet:
quelle
constructor
Attribut hat in JS keine besondere Bedeutung, daher können Sie es auch aufrufenbananashake
. Der einzige Unterschied ist , dass der Motor initialisiert automatischconstructor
auf ,f.prototype
wenn Sie eine Funktion deklarierenf
. Es kann jedoch jederzeit überschrieben werden.constructor
bedeutet , dass es funktioniert in JS besondere Bedeutung hat, so ziemlich per Definition.return new this.constructor(this.name);
anstelle von verwendenreturn new Person(this.name);
. Dathis.constructor
es sich um dieStudent
Funktion handelt (weil Sie sie mit festgelegt habenStudent.prototype.constructor = Student;
),copy
ruft dieStudent
Funktion die Funktion auf. Ich bin mir nicht sicher, was Sie mit dem//just as bad
Kommentar vorhatten.Student
Konstruktor ein zusätzliches Argument hinzugefügt hätte wie :Student(name, id)
? Müssen wir dann diecopy
Funktion überschreiben , diePerson
Version aus ihr heraus aufrufen und dann auch die zusätzlicheid
Eigenschaft kopieren ?Ja und nein.
In ES5 und früheren Versionen wurde JavaScript selbst nicht verwendet
constructor
für nichts verwendet. Es wurde definiert, dass das Standardobjekt in derprototype
Eigenschaft einer Funktion es haben würde und dass es auf die Funktion zurückgreifen würde, und das war es . Nichts anderes in der Spezifikation bezog sich überhaupt darauf.Dies änderte sich in ES2015 (ES6), das damit begann, es in Bezug auf Vererbungshierarchien zu verwenden.
Promise#then
Verwendet zum Beispiel dieconstructor
Eigenschaft des Versprechens Sie es fordern (via SpeciesConstructor ) , wenn das neue Versprechen Gebäude zurückzukehren. Es ist auch an der Subtypisierung von Arrays beteiligt (über ArraySpeciesCreate ).Außerhalb der Sprache selbst wurde sie manchmal verwendet, wenn versucht wurde, generische "Klon" -Funktionen zu erstellen, oder nur allgemein, wenn sie sich auf das beziehen wollten, von dem sie glaubten, dass es die Konstruktorfunktion des Objekts ist. Ich habe die Erfahrung gemacht, dass es selten ist, aber manchmal wird es benutzt.
Es ist standardmäßig vorhanden. Sie müssen es nur zurücksetzen, wenn Sie das Objekt in der Eigenschaft einer Funktion ersetzen
prototype
:Wenn Sie dies nicht tun:
...
Student.prototype.constructor
erbt dann vonPerson.prototype
dem (vermutlich) hatconstructor = Person
. Es ist also irreführend. Und wenn Sie etwas unterordnen, das es verwendet (wiePromise
oderArray
), und nichtclass
¹ verwenden (was dies für Sie erledigt), sollten Sie natürlich sicherstellen, dass Sie es richtig einstellen. Also im Grunde: Es ist eine gute Idee.Es ist in Ordnung, wenn nichts in Ihrem Code (oder Bibliothekscode, den Sie verwenden) es verwendet. Ich habe immer dafür gesorgt, dass es richtig verkabelt ist.
Mit dem Schlüsselwort von ES2015 (auch bekannt als ES6) müssen
class
wir es natürlich die meiste Zeit nicht mehr verwenden, da es für uns dann behandelt wird¹ "... wenn Sie etwas unterordnen, das es verwendet (wie
Promise
oderArray
) und nichtclass
..." - Das ist möglich , aber es ist ein echter Schmerz (und ein bisschen albern). Du musst benutzenReflect.construct
.quelle
TLDR; Nicht besonders notwendig, wird aber wahrscheinlich auf lange Sicht helfen, und es ist genauer, dies zu tun.
HINWEIS: Viel bearbeitet, da meine vorherige Antwort verwirrend geschrieben war und einige Fehler aufwies, die ich in meiner Eile zur Beantwortung verpasst hatte. Vielen Dank an diejenigen, die auf einige ungeheure Fehler hingewiesen haben.
Grundsätzlich geht es darum, Unterklassen in Javascript korrekt zu verdrahten. Wenn wir eine Unterklasse erstellen, müssen wir einige funky Dinge tun, um sicherzustellen, dass die prototypische Delegierung ordnungsgemäß funktioniert, einschließlich des Überschreibens eines
prototype
Objekts. Das Überschreiben einesprototype
Objekts umfasst dieconstructor
, daher müssen wir die Referenz korrigieren.Lassen Sie uns kurz durchgehen, wie 'Klassen' in ES5 funktionieren.
Angenommen, Sie haben eine Konstruktorfunktion und ihren Prototyp:
Wenn Sie den Konstruktor zum Instanziieren aufrufen, sagen Sie
Adam
:Das
new
mit 'Person' aufgerufene Schlüsselwort führt den Personenkonstruktor grundsätzlich mit einigen zusätzlichen Codezeilen aus:Wenn wir
console.log(adam.species)
, wird die Suche in der Fail -adam
Instanz, und schauen Sie die prototypische Kette der jeweils.prototype
, das istPerson.prototype
- undPerson.prototype
hat eine.species
Eigenschaft, so dass die Lookup auf Erfolg haben wirdPerson.prototype
. Es wird dann protokolliert'human'
.Hier zeigt der
Person.prototype.constructor
Wille richtig aufPerson
.Nun also der interessante Teil, die sogenannte "Unterklasse". Wenn wir eine
Student
Klasse erstellen möchten, dh eine Unterklasse derPerson
Klasse mit einigen zusätzlichen Änderungen, müssen wir sicherstellen, dass dieStudent.prototype.constructor
Punkte für die Genauigkeit auf Student zeigen.Das macht es nicht alleine. Wenn Sie eine Unterklasse erstellen, sieht der Code folgendermaßen aus:
Wenn Sie
new Student()
hier aufrufen, wird ein Objekt mit allen gewünschten Eigenschaften zurückgegeben. Hier, wenn wir überprüfeneve instanceof Person
, würde es zurückkehrenfalse
. Wenn wir versuchen, darauf zuzugreifeneve.species
, würde es zurückkehrenundefined
.Mit anderen Worten, wir müssen die Delegierung so verkabeln, dass
eve instanceof Person
true zurückgegeben wird und Instanzen derStudent
Delegierung korrekt anStudent.prototype
und dann gesendet werdenPerson.prototype
.ABER da wir es mit dem
new
Schlüsselwort aufrufen , erinnern Sie sich, was dieser Aufruf hinzufügt? Es würde heißenObject.create(Student.prototype)
, wie wir diese Delegationsbeziehung zwischenStudent
und aufbauenStudent.prototype
. Beachten Sie, dass im MomentStudent.prototype
leer ist. So aufzublicken.species
eine InstanzStudent
fehlschlagen würde , wie es Delegierten nurStudent.prototype
, und die.species
Eigenschaft existiert nicht aufStudent.prototype
.Wenn wir assign tun
Student.prototype
zuObject.create(Person.prototype)
,Student.prototype
selbst dann DelegiertenPerson.prototype
und aufzublickeneve.species
zurückkehren wird ,human
wie wir es erwarten. Vermutlich möchten wir, dass es von Student.prototype AND Person.prototype erbt. Also müssen wir das alles beheben.Jetzt funktioniert die Delegation, aber wir überschreiben
Student.prototype
mit einem vonPerson.prototype
. Wenn wir also anrufenStudent.prototype.constructor
, würde es aufPerson
statt zeigenStudent
. Dies ist , warum wir es beheben müssen.In ES5 ist unsere
constructor
Eigenschaft eine Referenz, die sich auf eine Funktion bezieht, die wir mit der Absicht geschrieben haben, ein 'Konstruktor' zu sein. Abgesehen von dem, was dienew
Schlüsselwort uns gibt, ist der Konstruktor ansonsten eine 'einfache' Funktion.In ES6 ist das
constructor
jetzt in die Art und Weise integriert, wie wir Klassen schreiben - wie in wird es als Methode bereitgestellt, wenn wir eine Klasse deklarieren. Dies ist einfach syntaktischer Zucker, aber es bietet uns einige Annehmlichkeiten wie den Zugang zu einem,super
wenn wir eine bestehende Klasse erweitern. Also würden wir den obigen Code so schreiben:quelle
eve instanceof Student
zurückgegebentrue
. Erläuterungen finden Sie unter stackoverflow.com/questions/35537995/… . Auch wenn Sie sagen,which is, at the moment, nothing
worauf beziehen Sie sich? Jede Funktion hat einen Prototyp. Wenn ich das überprüfeStudent.prototype
, ist es etwas.Object.create(Person.prototype)
, dieStudent.prototype
leer ist . Wenn wir uns also anmeldeneve.species
, wird es nicht ordnungsgemäß an seine Oberklasse Person delegiert, und es wird nicht protokolliert'human'
. Vermutlich wollen wir, dass jede Unterklasse von ihrem Prototyp und auch von ihrem Super-Prototyp erbt.which is, at the moment, nothing
meinte ich, dass dasStudent.prototype
Objekt leer ist.Student.prototype
zuObject.create(Person.prototype)
- das ist, wenn man die gleiche Art und Weise erinnert, werden alle Instanzen der Person eingerichtet DelegiertenPerson.prototype
- eine Eigenschaft auf einer Instanz des NachschlagenStudent
würde delegiert nurStudent.prototype
. Alsoeve.species
wird seine Suche fehlschlagen. Wenn wir es zuweisen, werden dieStudent.prototype
Delegierten selbst anPerson.prototype
und das Nachschlageneve.species
wird zurückkehrenhuman
.instance
, damit diese genau sind , wenn Sie überprüfen, ob eine Instanz der Konstruktor' Unterklassen 'ist." Nein,instanceof
benutzt nichtconstructor
. "Wenn wir jedoch den .prototype.constructor des Schülers nachschlagen, würde er immer noch auf Person verweisen." Nein, das wird es seinStudent
. Ich verstehe den Sinn dieses Beispiels nicht. Das Aufrufen einer Funktion in einem Konstruktor ist keine Vererbung. "In ES6 ist der Konstruktor jetzt eine tatsächliche Funktion anstelle eines Verweises auf eine Funktion." Äh was?Ich würde nicht zustimmen. Der Prototyp muss nicht eingestellt werden. Nehmen Sie genau denselben Code, aber entfernen Sie die Zeile prototype.constructor. Ändert sich etwas? Nehmen Sie jetzt die folgenden Änderungen vor:
und am Ende des Testcodes ...
Die Farbe wird blau sein.
Eine Änderung am prototype.constructor macht meiner Erfahrung nach nicht viel, es sei denn, Sie machen sehr spezifische, sehr komplizierte Dinge, die wahrscheinlich sowieso keine gute Praxis sind :)
Bearbeiten: Nachdem Sie ein wenig im Web gestöbert und experimentiert haben, sieht es so aus, als würden die Leute den Konstruktor so einstellen, dass er wie das aussieht, was mit "neu" erstellt wird. Ich denke, ich würde argumentieren, dass das Problem dabei ist, dass Javascript eine Prototypsprache ist - es gibt keine Vererbung. Die meisten Programmierer haben jedoch einen Programmierhintergrund, der die Vererbung als "Weg" bezeichnet. Wir haben uns also alle möglichen Dinge ausgedacht, um diese prototypische Sprache zu einer "klassischen" Sprache zu machen. Zum Beispiel die Erweiterung von "Klassen". In dem Beispiel, das sie gegeben haben, ist ein neuer Schüler eine Person - es geht nicht von einem anderen Schüler aus. Der Schüler dreht sich alles um die Person, und was auch immer die Person ist, der Schüler ist es auch. Erweitern Sie den Schüler und was auch immer Sie '
Crockford ist ein bisschen verrückt und übereifrig, aber lesen Sie einige der Dinge, die er geschrieben hat, ernsthaft durch. Dadurch werden Sie diese Dinge ganz anders betrachten.
quelle
Dies hat die große Gefahr, dass, wenn Sie geschrieben haben
aber dann, wenn es einen Lehrer gab, dessen Prototyp auch Person war und Sie geschrieben haben
dann ist der Schülerkonstruktor jetzt Lehrer!
Bearbeiten: Sie können dies vermeiden, indem Sie sicherstellen, dass Sie die Schüler- und Lehrerprototypen mithilfe neuer Instanzen der mit Object.create erstellten Person-Klasse wie im Mozilla-Beispiel festgelegt haben.
quelle
Student.prototype = Object.create(...)
wird in dieser Frage angenommen. Diese Antwort fügt nichts als mögliche Verwirrung hinzu.Object.create(...)
wird in dem MDN-Artikel verwendet, der die Frage hervorgebracht hat, jedoch nicht in der Frage selbst. Ich bin sicher, dass viele Leute nicht durchklicken.Bisher herrscht noch Verwirrung.
Folgen Sie dem ursprünglichen Beispiel, da Sie ein vorhandenes Objekt haben
student1
als:Angenommen, Sie möchten nicht wissen, wie
student1
erstellt wird, Sie möchten nur ein anderes Objekt wie dieses. Sie können die Konstruktoreigenschaft vonstudent1
like verwenden:Hier können die Eigenschaften
Student
nicht abgerufen werden, wenn die Konstruktoreigenschaft nicht festgelegt ist. Vielmehr wird einPerson
Objekt erstellt.quelle
Ich habe ein schönes Codebeispiel dafür erhalten, warum es wirklich notwendig ist, den Prototyp-Konstruktor festzulegen.
quelle
createNewCar
Methode schafft Fabriken!? Auch dies sieht so aus, als hätte esvar audiFactory = new CarFactory("Audi")
eher als Vererbung verwendet werden sollen.this.constructor
intern verwendet, daher ist es nicht verwunderlich, dass es festgelegt werden muss. Haben Sie ein Beispiel ohne es?Heutzutage sind keine gezuckerten Funktionsklassen oder die Verwendung von "Neu" erforderlich. Verwenden Sie Objektliterale.
Der Objektprototyp ist bereits eine 'Klasse'. Wenn Sie ein Objektliteral definieren, ist es bereits eine Instanz des Prototypobjekts. Diese können auch als Prototyp eines anderen Objekts usw. fungieren.
Das ist eine Lektüre wert :
quelle
Es ist notwendig, wenn Sie eine Alternative zu
toString
ohne Monkeypatching benötigen :quelle
foo.constructor()
??EDIT, ich habe mich tatsächlich geirrt. Das Kommentieren der Zeile ändert nichts an ihrem Verhalten. (Ich habe es getestet)
Ja, das ist notwendig. Wenn Sie das tun
Student.prototype.constructor
wirdPerson
. DaherStudent()
würde ein Aufruf ein Objekt zurückgeben, das von erstellt wurdePerson
. Wenn du es dann tustStudent.prototype.constructor
wird zurückgesetzt aufStudent
. Wenn SieStudent()
es jetzt ausführenStudent
, wird der übergeordnete Konstruktor aufgerufen undParent()
das korrekt geerbte Objekt zurückgegeben. Wenn SieStudent.prototype.constructor
vor dem Aufruf nicht zurückgesetzt haben, erhalten Sie ein Objekt, für das keine der Eigenschaften festgelegt istStudent()
.quelle
Bei einfacher Konstruktorfunktion:
Standardmäßig (aus der Spezifikation https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ) erhalten alle Prototypen automatisch eine Eigenschaft namens Konstruktor, die auf die Funktion zurück verweist was es ist eine Eigenschaft. Abhängig vom Konstruktor können dem Prototyp andere Eigenschaften und Methoden hinzugefügt werden, was nicht sehr üblich ist, aber dennoch für Erweiterungen zulässig ist.
Also einfach antworten: Wir müssen sicherstellen, dass der Wert in prototype.constructor korrekt eingestellt ist, wie es in der Spezifikation angenommen wird.
Müssen wir diesen Wert immer richtig einstellen? Es hilft beim Debuggen und macht die interne Struktur gegenüber der Spezifikation konsistent. Wir sollten auf jeden Fall, wenn unsere API von den Drittanbietern verwendet wird, aber nicht wirklich, wenn der Code schließlich zur Laufzeit ausgeführt wird.
quelle
Hier ist ein Beispiel von MDN, das ich sehr hilfreich fand, um seine Verwendung zu verstehen.
In JavaScript gibt es
async functions
das AsyncFunction- Objekt.AsyncFunction
ist kein globales Objekt, aber man kann es mithilfe derconstructor
Eigenschaft abrufen und verwenden.quelle
Es ist nicht erforderlich. Es ist nur eines der vielen Dinge, die traditionelle OOP-Champions tun, um zu versuchen, die prototypische Vererbung von JavaScript in eine klassische Vererbung umzuwandeln. Das einzige was das folgende ist
tut, ist, dass Sie jetzt eine Referenz des aktuellen "Konstruktors" haben.
In Waynes Antwort, die als richtig markiert wurde, könnten Sie genau dasselbe tun wie im folgenden Code
mit dem folgenden Code (ersetzen Sie einfach this.constructor durch Person)
Gott sei Dank können Puristen mit klassischer Vererbung von ES6 die nativen Operatoren der Sprache wie class, extensiv und super verwenden, und wir müssen nicht wie prototype.constructor-Korrekturen und übergeordnete Referenzen sehen.
quelle