Ich versuche, hinter den Kulissen von Javascript zu verstehen und die Entstehung eingebauter Objekte, insbesondere Objekt und Funktion, und die Beziehung zwischen ihnen zu verstehen .
Als ich las, dass alle eingebauten Objekte wie Array, String usw. Erweiterungen (geerbt) von Object sind, ging ich davon aus, dass Object das erste eingebaute Objekt ist, das erstellt wird, und der Rest der Objekte von ihm erbt. Es macht jedoch keinen Sinn, wenn Sie wissen, dass Objekte nur durch Funktionen erstellt werden können, aber dann sind Funktionen auch nichts anderes als Objekte von Function. Es fing an, wie ein Dilemma von Henne und Huhn zu klingen.
Die andere äußerst verwirrende Sache ist, wenn ich console.log(Function.prototype)
eine Funktion drucke, aber wenn ich sie drucke console.log(Object.prototype)
, druckt sie ein Objekt. Warum ist Function.prototype
eine Funktion eigentlich ein Objekt?
Außerdem ist laut Mozilla-Dokumentation jedes Javascript function
eine Erweiterung des Function
Objekts, aber wenn Sie console.log(Function.prototype.constructor)
es erneut ausführen, handelt es sich um eine Funktion. Wie kannst du nun etwas benutzen, um es selbst zu erschaffen (Mind = Blown)?
Das Letzte, was Function.prototype
ist eine Funktion, aber ich kann auf die constructor
Funktion zugreifen , indem ich Function.prototype.constructor
tue, das heißt, es Function.prototype
ist eine Funktion, die das prototype
Objekt zurückgibt
quelle
Function.prototype
sie eine Funktion sein kann und innere Felder hat. Nein, Sie führen die Prototypfunktion nicht aus, wenn Sie die Struktur durchgehen. Denken Sie schließlich daran, dass es eine Engine gibt, die Javascript interpretiert, sodass Objekt und Funktion wahrscheinlich innerhalb der Engine erstellt werden und nicht aus Javascript und speziellen Referenzen wieFunction.prototype
undObject.prototype
möglicherweise nur auf besondere Weise von der Engine interpretiert werden.Antworten:
Es ist kompliziert, leicht zu missverstehen und viele Anfänger-Javascript-Bücher verstehen es falsch. Vertrauen Sie also nicht allem, was Sie lesen.
Ich war einer der Implementierer der JS-Engine von Microsoft in den neunziger Jahren und Mitglied des Standardisierungsausschusses und habe eine Reihe von Fehlern begangen, als ich diese Antwort zusammengestellt habe. (Obwohl ich seit über 15 Jahren nicht mehr daran gearbeitet habe, kann mir vielleicht vergeben werden.) Es ist kniffliges Zeug. Aber sobald Sie die Vererbung von Prototypen verstanden haben, ist alles sinnvoll.
Werfen Sie zunächst alles weg, was Sie über klassenbasierte Vererbung wissen. JS verwendet prototypbasierte Vererbung.
Stellen Sie als nächstes sicher, dass Sie in Ihrem Kopf eine sehr klare Definition dessen haben, was "Vererbung" bedeutet. Leute, die an OO-Sprachen wie C # oder Java oder C ++ gewöhnt sind, denken, dass Vererbung Subtypisierung bedeutet, aber Vererbung bedeutet nicht Subtypisierung. Vererbung bedeutet, dass die Mitglieder einer Sache auch Mitglieder einer anderen Sache sind . Dies bedeutet nicht unbedingt , dass zwischen diesen Dingen eine Untertypisierungsbeziehung besteht! So viele Missverständnisse in der Typentheorie sind das Ergebnis von Menschen, die nicht erkennen, dass es einen Unterschied gibt.
Das ist einfach falsch. Einige Objekte werden nicht durch Aufrufen
new F
einer Funktion erstelltF
. Einige Objekte werden von der JS-Laufzeit aus dem Nichts erstellt. Es gibt Eier, die von keinem Huhn gelegt wurden . Sie wurden gerade von der Laufzeit erstellt, als sie gestartet wurde.Sagen wir, was die Regeln sind und vielleicht wird das helfen.
null
.prototype
Mitglied eines Objekts ist normalerweise nicht der Prototyp des Objekts.prototype
Element eines Funktionsobjekts F das Objekt, das zum Prototyp des von erstellten Objekts wirdnew F()
.__proto__
Mitglied, das wirklich ihren Prototyp angibt. (Dies ist jetzt veraltet. Verlassen Sie sich nicht darauf.)prototype
.Function.prototype
.Fassen wir zusammen.
Object
istFunction.prototype
Object.prototype
ist das Objektprototypobjekt.Object.prototype
istnull
Function
istFunction.prototype
- dies ist eine der seltenen Situationen, in denenFunction.prototype
tatsächlich der Prototyp von istFunction
!Function.prototype
ist das Funktionsprototypobjekt.Function.prototype
istObject.prototype
Nehmen wir an, wir machen eine Funktion Foo.
Foo
istFunction.prototype
.Foo.prototype
ist das Foo-Prototypobjekt.Foo.prototype
istObject.prototype
.Nehmen wir an, wir sagen
new Foo()
Foo.prototype
Stellen Sie sicher, dass dies sinnvoll ist. Lass es uns zeichnen. Ovale sind Objektinstanzen. Kanten
__proto__
bedeuten entweder "der Prototyp von" oderprototype
"dieprototype
Eigenschaft von".Alles, was Sie zur Laufzeit tun müssen, ist, alle diese Objekte zu erstellen und ihre verschiedenen Eigenschaften entsprechend zuzuweisen. Ich bin sicher, Sie können sehen, wie das gemacht werden würde.
Schauen wir uns nun ein Beispiel an, das Ihr Wissen testet.
Was druckt das?
Nun, was heißt
instanceof
das?honda instanceof Car
bedeutet "istCar.prototype
gleich einem Objekt inhonda
der Prototypenkette?"Ja ist es.
honda
Der Prototyp istCar.prototype
, also sind wir fertig. Dies druckt wahr.Was ist mit dem zweiten?
honda.constructor
existiert nicht, so konsultieren wir den Prototyp, der istCar.prototype
. BeimCar.prototype
Erstellen des Objekts wurde ihm automatisch die Eigenschaft "constructor
gleich"Car
zugewiesen. Dies ist also der Fall.Was ist nun damit?
Was druckt dieses Programm?
Wieder
lizard instanceof Reptile
bedeutet "istReptile.prototype
gleich einem Objekt inlizard
der Prototypenkette?"Ja ist es.
lizard
Der Prototyp istReptile.prototype
, also sind wir fertig. Dies druckt wahr.Und jetzt?
Sie könnten denken, dass dies auch wahr ist, da
lizard
mit konstruiert wurde,new Reptile
aber Sie würden sich irren. Grund es aus.lizard
eineconstructor
Immobilie? Nein, deshalb schauen wir uns den Prototyp an.lizard
istReptile.prototype
, was istAnimal
.Animal
eineconstructor
Immobilie? Also schauen wir uns den Prototyp an.Animal
istObject.prototype
, undObject.prototype.constructor
wird zur Laufzeit erstellt und ist gleichObject
.Wir hätten es
Reptile.prototype.constructor = Reptile;
irgendwann sagen sollen , aber daran haben wir uns nicht erinnert!Stellen Sie sicher, dass alles für Sie sinnvoll ist. Zeichne ein paar Kästchen und Pfeile, wenn es immer noch verwirrend ist.
Der Funktionsprototyp ist als eine Funktion definiert, die beim Aufruf zurückgegeben wird
undefined
. Wir wissen bereits, dass dies seltsamerweiseFunction.prototype
derFunction
Prototyp ist. AlsoFunction.prototype()
ist das legal und wenn du es tust, kommst duundefined
zurück. Es ist also eine Funktion.Der
Object
Prototyp besitzt diese Eigenschaft nicht. es ist nicht aufrufbar. Es ist nur ein Objekt.Function.prototype.constructor
ist einfachFunction
offensichtlich. UndFunction
ist eine Funktion.Sie denken darüber nach . Es ist lediglich erforderlich, dass die Laufzeit beim Start eine Reihe von Objekten erstellt. Objekte sind lediglich Nachschlagetabellen, die Zeichenfolgen mit Objekten verknüpfen. Wenn die Laufzeit startet, alles hat, es zu tun ist , ein paar Dutzend leere Objekte erstellen und dann die Start zuweisen
prototype
,__proto__
,constructor
, und so weiter Eigenschaften jedes Objekts , bis sie das Diagramm zu machen , dass sie machen müssen.Es ist hilfreich, wenn Sie das Diagramm, das ich Ihnen oben gegeben habe, nehmen und ihm
constructor
Kanten hinzufügen . Sie werden schnell feststellen, dass dies ein sehr einfaches Objektdiagramm ist und dass die Laufzeitumgebung keine Probleme damit hat, es zu erstellen.Eine gute Übung wäre es, es selbst zu tun. Hier, ich werde dich anfangen. Wir werden verwenden
my__proto__
, um "das Prototypobjekt von" und "die Prototypeneigenschaft von"myprototype
zu bedeuten.Und so weiter. Können Sie den Rest des Programms ausfüllen, um eine Gruppe von Objekten zu erstellen, die dieselbe Topologie wie die "echten" in Javascript eingebauten Objekte haben? Wenn Sie dies tun, werden Sie feststellen, dass es extrem einfach ist.
Objekte in JavaScript sind nur Nachschlagetabellen, die Zeichenfolgen mit anderen Objekten verknüpfen . Das ist es! Hier gibt es keine Magie. Sie werden in Knoten gefesselt, weil Sie sich Einschränkungen vorstellen, die eigentlich nicht existieren, so dass jedes Objekt von einem Konstruktor erstellt werden musste.
Funktionen sind nur Objekte, die eine zusätzliche Fähigkeit haben: aufgerufen zu werden. Gehen Sie also Ihr kleines Simulationsprogramm durch und fügen Sie
.mycallable
jedem Objekt eine Eigenschaft hinzu, die angibt, ob es aufrufbar ist oder nicht. So einfach ist das.quelle
__proto__
. Der__proto__
des Objektprototyps ist null. Das__proto__
vonnew X()
istX.prototype
. Alle Funktionsobjekte haben den Funktionsprototyp mit__proto__
Ausnahme des Funktionsprototyps.Object
undFunction
und der Funktionsprototyp sind Funktionen. Diese Regeln sind alle unkompliziert und bestimmen die Topologie des Diagramms der ursprünglichen Objekte.Sie haben bereits viele ausgezeichnete Antworten, aber ich möchte nur eine kurze und klare Antwort auf Ihre Antwort geben, wie all dies funktioniert, und diese Antwort lautet:
MAGIE!!!
Wirklich, das war's.
Die Leute , die Ausführung Motoren ECMAScript implementieren müssen implementieren ECMAScript-Regeln, aber nicht zu halten , indem sie in deren Umsetzung.
Die ECMAScript-Spezifikation besagt, dass A von B erbt, B jedoch eine Instanz von A? Kein Problem! Erstellen Sie zuerst A mit einem Prototypzeiger von
NULL
, erstellen Sie B als Instanz von A, und richten Sie den Prototypzeiger von A so ein, dass er anschließend auf B zeigt. Kinderleicht.Sie sagen, aber warten Sie, es gibt keine Möglichkeit, den Prototypzeiger in ECMAScript zu ändern! Aber hier ist die Sache: Dieser Code läuft nicht auf der ECMAScript-Engine, dieser Code ist die ECMAScript-Engine. Es hat Zugriff auf Interna der Objekte, über die ECMAScript-Code, der auf der Engine ausgeführt wird, nicht verfügt. Kurzum: Er kann machen, was er will.
Übrigens, wenn Sie es wirklich wollen, müssen Sie dies nur einmal tun: Danach können Sie beispielsweise Ihren internen Speicher sichern und diesen bei jedem Start Ihrer ECMAScript-Engine laden.
Dies gilt auch dann, wenn die ECMAScript-Engine selbst in ECMAScript geschrieben wurde (wie dies beispielsweise bei Mozilla Narcissus der Fall ist). Selbst dann hat der ECMAScript-Code, der die Engine implementiert , noch uneingeschränkten Zugriff auf die von ihr implementierte Engine , obwohl er natürlich keinen Zugriff auf die Engine hat , auf der sie ausgeführt wird .
quelle
Aus ECMA-Spezifikation 1
Ich sehe nicht ein, wie es klarer sein könnte !!!
</sarcasm>
Weiter unten sehen wir:
So können wir sehen, dass ein Prototyp ein Objekt ist, aber nicht unbedingt ein Funktionsobjekt.
Wir haben auch diesen interessanten Leckerbissen
http://www.ecma-international.org/ecma-262/8.0/index.html#sec-object-objects
und
quelle
sarcasm
Spitzname ansonsten ist dieser Text für einen Anfänger wirklich ziemlich undurchsichtig.Die folgenden Typen umfassen jeden Wert in JavaScript:
boolean
number
undefined
(die den einzelnen Wert enthältundefined
)string
symbol
(abstrakte eindeutige "Dinge", die durch Referenz verglichen werden)object
Jedes Objekt (dh alles) in JavaScript hat einen Prototyp, der eine Art Objekt ist.
Der Prototyp enthält Funktionen, die auch eine Art Objekt 1 sind .
Objekte haben auch einen Konstruktor, der eine Funktion und damit eine Art Objekt ist.
Es ist alles rekursiv, aber die Implementierung kann dies automatisch tun, da sie im Gegensatz zu JavaScript-Code Objekte erstellen kann, ohne JavaScript-Funktionen aufrufen zu müssen (da Objekte nur Speicher sind, den die Implementierung steuert).
Die meisten Objektsysteme in vielen dynamisch typisierten Sprachen sind wie folgt zirkulär 2 . Zum Beispiel in Python, sind Klassen - Objekte und die Klasse der Klassen ist
type
, sotype
ist deshalb eine Instanz von sich selbst.Die beste Idee ist, nur die Tools zu verwenden, die die Sprache bereitstellt, und nicht zu viel darüber nachzudenken, wie sie dorthin gelangt sind.
1 Funktionen sind etwas Besonderes, weil sie aufrufbar sind und die einzigen Werte sind, die undurchsichtige Daten enthalten können (ihren Körper und möglicherweise einen Abschluss).
2 Eigentlich handelt es sich eher um ein gequältes, nach hinten gebogenes, verzweigtes Band, aber "rund" ist nah genug.
quelle