Heim >Web-Frontend >js-Tutorial >Vertiefendes Verständnis der JavaScript-Reihe (17): Detaillierte Einführung in die objektorientierte Programmierung_Grundkenntnisse
Einführung
In diesem Artikel betrachten wir verschiedene Aspekte der objektorientierten Programmierung in ECMAScript (obwohl dieses Thema bereits in vielen Artikeln behandelt wurde). Wir werden diese Fragen eher aus einer theoretischen Perspektive betrachten. Insbesondere werden wir Algorithmen zur Objekterstellung betrachten, wie Objekte miteinander in Beziehung stehen (einschließlich grundlegender Beziehungen – Vererbung), die auch in Diskussionen verwendet werden können (was hoffentlich einige frühere konzeptionelle Unklarheiten über OOP in JavaScript beseitigen wird).
Englischer Originaltext:http://dmitrysoshnikov.com/ecmascript/chapter-7-1-oop-general-theory/
Einführung, Paradigma und Ideen
Bevor wir die technische Analyse von OOP in ECMAScript durchführen, müssen wir einige grundlegende Merkmale von OOP beherrschen und die Hauptkonzepte in der Einleitung klären.
ECMAScript unterstützt eine Vielzahl von Programmiermethoden, einschließlich strukturierter, objektorientierter, funktionaler, imperativer usw. In einigen Fällen unterstützt es auch aspektorientierte Programmierung, aber in diesem Artikel geht es um objektorientierte Programmierung. orientierte Programmierung in ECMAScript Definition:
ECMAScript ist eine objektorientierte Programmiersprache, die auf der Implementierung von Prototypen basiert.
Es gibt viele Unterschiede zwischen prototypbasierten OOP- und statischen klassenbasierten Ansätzen. Werfen wir einen Blick auf ihre Unterschiede im Detail.
Basierend auf Klassenattributen und basierend auf Prototypen
Beachten Sie, dass im vorherigen Satz auf einen wichtigen Punkt hingewiesen wurde – vollständig basierend auf statischen Klassen. Unter dem Wort „statisch“ verstehen wir statische Objekte und statische Klassen, die stark typisiert sind (obwohl nicht erforderlich).
In Bezug auf diese Situation haben viele Dokumente im Forum betont, dass dies der Hauptgrund ist, warum sie Einwände gegen den Vergleich von „Klassen mit Prototypen“ in JavaScript haben, obwohl ihre Implementierungen unterschiedlich sind (z. B. basierend auf dynamischen Klassen (Python und Ruby). Sie stehen nicht allzu sehr im Gegensatz zum Fokus (einige Bedingungen sind geschrieben, obwohl es bestimmte Denkunterschiede gibt, JavaScript ist nicht so alternativ geworden), aber der Fokus ihres Gegensatzes liegt auf statischen Klassen vs. dynamischen Prototypen), um genau zu sein, auf dem Mechanismus Durch die Betrachtung einer statischen Klasse (z. B. C, JAVA) und ihrer Untergeordneten und Methodendefinitionen können wir den genauen Unterschied zwischen ihr und einer prototypbasierten Implementierung erkennen.
Aber lasst uns sie einzeln auflisten. Betrachten wir allgemeine Prinzipien und die Hauptkonzepte dieser Paradigmen.
Basierend auf statischer Klasse
Im klassenbasierten Modell gibt es ein Konzept von Klassen und Instanzen. Instanzen von Klassen werden oft auch als Objekte oder Instanzen bezeichnet.
Klassen und Objekte
Eine Klasse repräsentiert eine Abstraktion einer Instanz (d. h. eines Objekts). In dieser Hinsicht ähnelt es ein wenig der Mathematik, aber wir nennen es einen Typ oder eine Klassifizierung.
Zum Beispiel (die Beispiele hier und unten sind Pseudocode):
Hierarchische Vererbung
Um die Wiederverwendung von Code zu verbessern, können Klassen von einer zur anderen erweitert und so zusätzliche Informationen hinzugefügt werden. Dieser Mechanismus wird als (hierarchische) Vererbung bezeichnet.
Wenn Sie eine Methode für eine Instanz einer Klasse aufrufen, suchen Sie normalerweise nach der Methode in der nativen Klasse. Wenn sie nicht gefunden wird, gehen Sie zur Suche zur direkten übergeordneten Klasse Die zu durchsuchende übergeordnete Klasse (z. B. in einer strikten Vererbungskette) lautet: Wenn die Spitze der Vererbung gefunden, aber noch nicht gefunden wurde, lautet das Ergebnis: Das Objekt weist kein ähnliches Verhalten auf und es gibt keine Möglichkeit, das Ergebnis zu erhalten.
Schlüsselkonzepte basierend auf Klassen
Daher haben wir die folgenden Schlüsselkonzepte:1. Vor dem Erstellen eines Objekts muss die Klasse zunächst deklariert werden
2. Daher wird das Objekt aus der Klasse erstellt, die in sein eigenes „Ikonogramm und seine Ähnlichkeit“ (Struktur und Verhalten) abstrahiert wird
3. Methoden werden über eine strenge, direkte und unveränderliche Vererbungskette verarbeitet
4. Die Unterklasse enthält alle Attribute in der Vererbungskette (auch wenn einige der Attribute von der Unterklasse nicht benötigt werden);
5. Eine Klasseninstanz erstellen (aufgrund des statischen Modells) kann die Eigenschaften (Eigenschaften oder Methoden) ihrer Instanz nicht ändern
6. Instanzen können (aufgrund des strengen statischen Modells) keine zusätzlichen Verhaltensweisen oder Attribute haben, die nicht in der der Instanz entsprechenden Klasse deklariert sind.
Sehen wir uns an, wie das OOP-Modell in JavaScript ersetzt werden kann, was wir basierend auf dem OOP-Prototyp vorschlagen.
Das Grundkonzept hier sind dynamische veränderbare Objekte. Transformationen (vollständige Transformationen, die nicht nur Werte, sondern auch Attribute umfassen) stehen in direktem Zusammenhang mit dynamischen Sprachen. Objekte wie die folgenden können alle ihre Eigenschaften (Eigenschaften, Methoden) unabhängig speichern, ohne dass eine Klasse erforderlich ist.
Ein Prototyp ist ein Objekt, das als primitive Kopie anderer Objekte verwendet wird. Wenn einige Objekte nicht über die erforderlichen eigenen Eigenschaften verfügen, kann der Prototyp als Delegat für diese Objekte verwendet werden und als Hilfsobjekt dienen .
Delegiert basierend
Jedes Objekt kann als Prototypobjekt für ein anderes Objekt verwendet werden, da ein Objekt seinen Prototyp zur Laufzeit leicht dynamisch ändern kann.
Beachten Sie, dass wir derzeit eher einen Überblick als eine spezifische Implementierung betrachten. Wenn wir spezifische Implementierungen in ECMAScript diskutieren, werden wir einige ihrer eigenen Merkmale sehen.Beispiel (Pseudocode):
Dieses Beispiel zeigt die wichtige Funktion und den Mechanismus des Prototyps als Hilfsobjektattribut, genau wie die Abfrage seiner eigenen Attribute. Im Vergleich zu seinen eigenen Attributen sind diese Attribute Delegatenattribute. Dieser Mechanismus wird als Delegate bezeichnet, und ein darauf basierendes Prototypmodell ist ein Delegate-Prototyp (oder delegiertenbasierter Prototyp). Der Referenzmechanismus wird hier als Senden einer Nachricht an ein Objekt bezeichnet. Wenn das Objekt keine Antwort erhält, wird es an den Prototyp delegiert, es zu finden (was ihn dazu zwingt, zu versuchen, auf die Nachricht zu antworten).
Die Wiederverwendung von Code wird in diesem Fall als delegiertenbasierte Vererbung oder prototypbasierte Vererbung bezeichnet. Da jedes Objekt als Prototyp verwendet werden kann, bedeutet dies, dass ein Prototyp auch einen eigenen Prototyp haben kann. Diese Prototypen werden zu einer sogenannten Prototypenkette miteinander verknüpft. Ketten sind ebenfalls hierarchisch wie statische Klassen, können jedoch leicht neu angeordnet werden, um die Hierarchie und Struktur zu ändern.
Wenn ein Objekt und seine Prototypenkette nicht auf das Senden von Nachrichten reagieren können, kann das Objekt das entsprechende Systemsignal aktivieren, das möglicherweise von anderen Delegierten in der Prototypenkette verarbeitet wird.
Dieses Systemsignal ist in vielen Implementierungen verfügbar, einschließlich Systemen, die auf dynamischen Klassen in Klammern basieren: #doesNotUnderstand in Smalltalk, method_missing in Python, __call in PHP und __noSuchMethod__-Implementierung in ECMAScript usw.
Beispiel (ECMAScript-Implementierung von SpiderMonkey):
Mit anderen Worten: Wenn die auf der statischen Klasse basierende Implementierung nicht auf die Nachricht reagieren kann, besteht die Schlussfolgerung darin, dass das aktuelle Objekt nicht über die erforderlichen Eigenschaften verfügt. Wenn Sie jedoch versuchen, es aus der Prototypenkette abzurufen, kann dies dennoch der Fall sein das Ergebnis erhalten, oder das Objekt besitzt diese Eigenschaft nach einer Reihe von Änderungen.
In Bezug auf ECMAScript lautet die spezifische Implementierung: Verwendung von delegiertenbasierten Prototypen. Wie wir jedoch anhand der Spezifikation und Implementierung sehen werden, haben sie auch ihre eigenen Eigenschaften.
Konkatenatives Modell
Ehrlich gesagt ist es notwendig, etwas über eine andere Situation zu sagen (sobald sie nicht in ECMASCript verwendet wird): die Situation, in der der Prototyp das native Objekt durch andere Objekte ersetzt. Bei der Wiederverwendung von Code handelt es sich in diesem Fall um eine echte Kopie (Klon) eines Objekts während der Objekterstellungsphase und nicht um eine Delegation. Diese Art von Prototyp wird als verketteter Prototyp bezeichnet. Das Kopieren aller Prototypeigenschaften eines Objekts kann seine Eigenschaften und Methoden weiter vollständig ändern, und der Prototyp kann sich auch selbst ändern (in einem delegiertenbasierten Modell ändert diese Änderung nicht das vorhandene Objektverhalten, sondern seine Prototypeigenschaften). Der Vorteil dieser Methode besteht darin, dass die Planungs- und Delegationszeit verkürzt werden kann. Der Nachteil besteht jedoch darin, dass die Speichernutzung hoch ist.
Ententyp
Im Vergleich zu Modellen, die auf statischen Klassen basieren, hat das Zurückgeben von Objekten, die diese Dinge tun können, nichts mit dem Typ (der Klasse) des Objekts zu tun, sondern ob es auf die Nachricht (das) reagieren kann ist, nachdem geprüft wurde, ob die Fähigkeit dazu ein Muss ist).
Zum Beispiel:
Dies ist der sogenannte Dock-Typ. Das heißt, Objekte können bei der Prüfung anhand ihrer eigenen Merkmale identifiziert werden und nicht anhand der Position des Objekts in der Hierarchie oder seiner Zugehörigkeit zu einem bestimmten Typ.
Schlüsselkonzepte basierend auf Prototypen
Werfen wir einen Blick auf die Hauptmerkmale dieses Ansatzes:
1. Das Grundkonzept ist Objekt
2. Das Objekt ist vollständig dynamisch und variabel (theoretisch kann es von einem Typ in einen anderen umgewandelt werden)
3. Objekte haben keine strengen Klassen, die ihre eigene Struktur und ihr eigenes Verhalten beschreiben. Objekte benötigen keine Klassen
4. Objekte haben keine Klassen, können aber Prototypen haben. Wenn sie nicht auf Nachrichten antworten können, können sie an den Prototyp
delegiert werden
5. Der Prototyp des Objekts kann jederzeit zur Laufzeit geändert werden;
6. Im delegiertenbasierten Modell wirkt sich die Änderung der Eigenschaften des Prototyps auf alle mit dem Prototyp verbundenen Objekte aus;
7. Im verketteten Prototypenmodell ist der Prototyp eine von anderen Objekten geklonte Originalkopie und wird darüber hinaus zu einer völlig unabhängigen Kopie des Originals. Die Transformation der Prototypeigenschaften hat keine Auswirkungen auf die daraus geklonten Objekte
8. Wenn die Nachricht nicht beantwortet werden kann, kann der Anrufer zusätzliche Maßnahmen ergreifen (z. B. die Planung ändern)
9. Der Ausfall von Objekten kann nicht anhand ihres Levels und ihrer Klassenzugehörigkeit bestimmt werden, sondern anhand der aktuellen Eigenschaften
Es gibt jedoch noch ein anderes Modell, das wir ebenfalls berücksichtigen sollten.
Basierend auf dynamischen Klassen
Wir glauben, dass die im obigen Beispiel gezeigte Unterscheidung „Klasse vs. Prototyp“ in diesem auf dynamischen Klassen basierenden Modell nicht so wichtig ist (insbesondere wenn die Prototypenkette unveränderlich ist, ist dies für eine genauere Unterscheidung dennoch erforderlich Betrachten Sie eine statische Klasse). Beispielsweise könnten auch Python oder Ruby (oder andere ähnliche Sprachen) verwendet werden. Diese Sprachen verwenden alle ein dynamisches klassenbasiertes Paradigma. In einigen Aspekten können wir jedoch sehen, dass einige auf dem Prototyp basierende Funktionen implementiert wurden.
Im folgenden Beispiel können wir sehen, dass wir eine Klasse (Prototyp) erweitern können, wodurch alle mit dieser Klasse verbundenen Objekte beeinflusst werden. Wir können dieses Objekt auch zur Laufzeit dynamisch ändern Objekt für den Delegierten) und so weiter.
Die Implementierung in Ruby ist ähnlich: Es werden auch volldynamische Klassen verwendet (in der aktuellen Version von Python funktioniert das Vergrößern von Klassen (Prototypen) übrigens im Gegensatz zu Ruby und ECMAScript nicht), wir können das Objekt komplett ändern (oder Klassen-)Eigenschaften (Hinzufügen von Methoden/Eigenschaften zur Klasse, und diese Änderungen wirken sich auf vorhandene Objekte aus), die Klasse eines Objekts kann jedoch nicht dynamisch geändert werden.
In diesem Artikel geht es jedoch nicht speziell um Python und Ruby, daher werden wir nicht mehr sagen und uns weiter mit ECMAScript selbst befassen.
Aber vorher müssen wir uns noch einmal mit dem „syntaktischen Zucker“ befassen, der in manchen OOPs vorkommt, da viele frühere Artikel über JavaScript diese Probleme häufig behandeln.
Der einzige falsche Satz, den es in diesem Abschnitt zu beachten gilt, ist: „JavaScript ist keine Klasse, es hat Prototypen, die Klassen ersetzen können.“ Es ist wichtig zu wissen, dass nicht alle klassenbasierten Implementierungen völlig unterschiedlich sind. Auch wenn wir vielleicht sagen: „JavaScript ist anders“, muss man auch berücksichtigen, dass es (neben dem Konzept der „Klassen“) noch andere verwandte Merkmale gibt .
Weitere Funktionen verschiedener OOP-Implementierungen
In diesem Abschnitt stellen wir kurz andere Funktionen und Methoden der Code-Wiederverwendung in verschiedenen OOP-Implementierungen vor, einschließlich OOP-Implementierungen in ECMAScript. Der Grund dafür ist, dass es bei der Implementierung von OOP in JavaScript einige gewohnheitsmäßige Einschränkungen gibt. Die einzige Hauptanforderung besteht darin, dass es technisch und ideologisch nachgewiesen werden muss. Es kann nicht gesagt werden, dass wir die syntaktische Zuckerfunktion nicht in anderen OOP-Implementierungen entdeckt haben, und wir haben voreilig angenommen, dass JavaScript keine reine OOP-Sprache ist. Das ist falsch.
Polymorph
Objekte haben in ECMAScript mehrere Bedeutungen von Polymorphismus.
Zum Beispiel kann eine Funktion auf verschiedene Objekte angewendet werden, genau wie die Eigenschaften des nativen Objekts (da der Wert beim Eintritt in den Ausführungskontext bestimmt wird):
Der sogenannte Parameterpolymorphismus beim Definieren einer Funktion entspricht allen Datentypen, außer dass er polymorphe Parameter akzeptiert (z. B. die Sortiermethode .sort des Arrays und seiner Parameter – polymorphe Sortierfunktion). Das obige Beispiel kann übrigens auch als eine Art parametrischer Polymorphismus betrachtet werden.
Methoden im Prototyp können als leer definiert werden, und alle erstellten Objekte sollten diese Methode neu definieren (implementieren) (d. h. „eine Schnittstelle (Signatur), mehrere Implementierungen“).
Polymorphismus hängt mit dem oben erwähnten Duck-Typ zusammen: d. h. der Typ und die Position des Objekts in der Hierarchie sind nicht so wichtig, aber wenn es alle erforderlichen Eigenschaften aufweist, kann es leicht akzeptiert werden (d. h. gemeinsame Schnittstellen sind wichtig). , Implementierungen können vielfältig sein).
Kapselung
Es gibt oft Missverständnisse über die Kapselung. In diesem Abschnitt besprechen wir einige syntaktische Zucker in OOP-Implementierungen – auch Modifikatoren genannt: In diesem Fall besprechen wir einige praktische „Zucker“ in OOP-Implementierungen – bekannte Modifikatoren: privat, geschützt und öffentlich (auch als Objektzugriff bekannt). Ebene oder Zugriffsmodifikator).
Hier möchte ich Sie an den Hauptzweck der Kapselung erinnern: Die Kapselung ist eine abstrakte Ergänzung, kein versteckter „böswilliger Hacker“, der etwas direkt in Ihre Klasse schreibt.
Das ist ein großer Fehler: Verwenden Sie hide, um sich zu verstecken.
Zugriffsebenen (privat, geschützt und öffentlich) wurden in vielen objektorientierten Programmen implementiert, um die Programmierung (wirklich sehr praktischer Syntaxzucker) zu erleichtern und Systeme abstrakter zu beschreiben und aufzubauen.
Dies ist in einigen Implementierungen zu sehen (z. B. Python und Ruby, die bereits erwähnt wurden). Einerseits (in Python) sind diese __private_protected-Attribute (benannt über die Unterstrichkonvention) von außen nicht zugänglich. Auf Python hingegen kann mit speziellen Regeln (_ClassName__field_name) von außen zugegriffen werden.
In Ruby: Einerseits besteht die Möglichkeit, private und geschützte Eigenschaften zu definieren. Andererseits gibt es auch spezielle Methoden (z. B. Instanz_Variable_Get, Instanz_Variable_Set, Senden usw.), um gekapselte Daten zu erhalten.
Der Hauptgrund ist, dass der Programmierer selbst die gekapselten (beachten Sie, dass ich ausdrücklich keine „versteckten“ Daten verwende) erhalten möchte. Wenn sich diese Daten in irgendeiner Weise falsch ändern oder Fehler aufweisen, liegt die volle Verantwortung beim Programmierer, nicht aber nur bei „Tippfehlern“ oder „nur bei der Änderung einiger Felder“. Wenn dies jedoch häufig vorkommt, handelt es sich um eine sehr schlechte Programmiergewohnheit und einen sehr schlechten Programmierstil, da es sich normalerweise lohnt, die öffentliche API zu verwenden, um mit dem Objekt zu „sprechen“.
Um es noch einmal zu wiederholen: Der Hauptzweck der Kapselung besteht darin, den Benutzer von Hilfsdaten zu abstrahieren und nicht, Hacker daran zu hindern, die Daten zu verbergen. Noch schlimmer ist, dass bei der Kapselung keine privaten Daten zum Ändern von Daten verwendet werden, um Softwaresicherheit zu erreichen.
Kapseln Sie Hilfsobjekte (teilweise). Wir nutzen minimale Kosten, Lokalisierung und prädiktive Änderungen, um Verhaltensänderungen in öffentlichen Schnittstellen möglich zu machen.
Darüber hinaus besteht der wichtige Zweck der Setter-Methode darin, komplexe Berechnungen zu abstrahieren. Beispielsweise ist der element.innerHTML-Setter – die abstrakte Anweisung – „Der HTML-Code in diesem Element ist jetzt der folgende Inhalt“ und die Setter-Funktion in der innerHTML-Eigenschaft schwer zu berechnen und zu überprüfen. In diesem Fall liegt das Problem hauptsächlich in der Abstraktion, aber es kommt auch zu einer Kapselung.
Das Konzept der Kapselung bezieht sich nicht nur auf OOP. Beispielsweise kann es sich um eine einfache Funktion handeln, die lediglich verschiedene Berechnungen kapselt und dadurch abstrakt macht (der Benutzer muss beispielsweise nicht wissen, wie die Funktion Math.round(...) implementiert ist, der Benutzer ruft sie einfach auf Es). Es handelt sich um eine Art Kapselung. Ich habe nicht gesagt, dass es „privat, geschützt und öffentlich“ ist.
Die aktuelle Version der ECMAScript-Spezifikation definiert nicht die privaten, geschützten und öffentlichen Modifikatoren.
In der Praxis ist es jedoch möglich, etwas namens „Mock JS Encapsulation“ zu sehen. Im Allgemeinen soll dieser Kontext verwendet werden (in der Regel der Konstruktor selbst). Leider wird diese „Mimikry“ oft implementiert und Programmierer können pseudo-absolut nicht-abstrakte „Getter/Setter-Methoden“ für Entitätseinstellungen erzeugen (ich wiederhole, es ist falsch):
Jeder versteht also, dass für jedes erstellte Objekt auch die getA/setA-Methoden erstellt werden, was auch der Grund für die Speichererweiterung (im Vergleich zur Prototypdefinition) ist. Obwohl theoretisch das Objekt im ersten Fall optimiert werden kann.
Darüber hinaus wird in einigen JavaScript-Artikeln häufig das Konzept der „privaten Methoden“ erwähnt. Hinweis: Der ECMA-262-3-Standard definiert kein Konzept der „privaten Methoden“.
In einigen Fällen kann es jedoch im Konstruktor erstellt werden, da JS eine ideologische Sprache ist – Objekte sind vollständig veränderbar und haben einzigartige Eigenschaften (unter bestimmten Bedingungen im Konstruktor können einige Objekte zusätzliche Methoden erhalten, andere nicht). ).
Wenn Kapselung in JavaScript außerdem immer noch als ein Verständnis missverstanden wird, das böswillige Hacker daran hindert, bestimmte Werte automatisch zu schreiben, anstatt die Setter-Methode zu verwenden, dann sind die sogenannten „versteckten“ und „privaten“ ist eigentlich nicht sehr „versteckt“, einige Implementierungen können den Wert in der relevanten Bereichskette (und den entsprechenden allen variablen Objekten) abrufen, indem sie den Kontext der Eval-Funktion aufrufen (kann auf SpiderMonkey1.7 getestet werden).
Alternativ ermöglicht die Implementierung den direkten Zugriff auf das aktive Objekt (z. B. Rhino) und der Wert der internen Variablen kann durch Zugriff auf die entsprechende Eigenschaft des Objekts geändert werden:
var _myPrivateData = 'testString';
Es wird oft verwendet, um den Ausführungskontext in Klammern zu setzen, aber für echte Hilfsdaten steht es nicht in direktem Zusammenhang mit dem Objekt, sondern eignet sich lediglich zum Abstrahieren von der externen API:
Mehrfachvererbung
Mehrfachvererbung ist ein sehr praktischer syntaktischer Zucker, um die Wiederverwendung von Code zu verbessern (wenn wir jeweils eine Klasse erben können, warum können wir dann nicht 10 gleichzeitig erben?). Aufgrund einiger Mängel der Mehrfachvererbung hat sie sich jedoch bei der Implementierung nicht durchgesetzt.
ECMAScript unterstützt keine Mehrfachvererbung (d. h. nur ein Objekt kann als direkter Prototyp verwendet werden), obwohl seine Vorgänger-Programmiersprache über eine solche Fähigkeit verfügt. Aber in einigen Implementierungen (z. B. SpiderMonkey) kann die Verwendung von __noSuchMethod__ anstelle der Prototypenkette zur Verwaltung von Planung und Delegation verwendet werden.
Mixins
Mixins sind eine praktische Möglichkeit, Code wiederzuverwenden. Mixins wurden als Alternativen zur Mehrfachvererbung vorgeschlagen. Jedes dieser einzelnen Elemente kann mit jedem Objekt gemischt werden, um deren Funktionalität zu erweitern (so können Objekte auch mit mehreren Mixins gemischt werden). Die ECMA-262-3-Spezifikation definiert nicht das Konzept von „Mixins“, aber gemäß der Definition von Mixins und ECMAScript über dynamisch veränderbare Objekte gibt es kein Hindernis dafür, Funktionen einfach mithilfe von Mixins zu erweitern.
Typisches Beispiel:
Bitte beachten Sie, dass ich diese Definitionen („mixin“, „mix“) in Anführungszeichen verwende, die in ECMA-262-3 erwähnt werden. Es gibt kein solches Konzept in der Spezifikation und es ist nicht „mix“, sondern wird häufig zum Erweitern von Objekten verwendet mit neuen Funktionen. (Das Konzept von Mixins in Ruby ist offiziell definiert. Mixins erstellen einen Verweis auf ein enthaltendes Modul, anstatt einfach alle Eigenschaften des Moduls in ein anderes Modul zu kopieren – tatsächlich: Erstellen eines zusätzlichen Objekts (Prototyps) für den Delegaten. ).
Eigenschaften
Merkmale ähneln im Konzept Mixins, verfügen jedoch über viele Funktionen (da Mixins per Definition angewendet werden können, können sie keinen Status enthalten, da dies zu Namenskonflikten führen kann). Laut ECMAScript folgen Traits und Mixins denselben Prinzipien, sodass die Spezifikation das Konzept von „Traits“ nicht definiert.
Schnittstelle
Die in einigen OOP implementierten Schnittstellen ähneln Mixins und Traits. Im Gegensatz zu Mixins und Traits zwingen Schnittstellen implementierende Klassen jedoch dazu, das Verhalten ihrer Methodensignaturen zu implementieren.
Schnittstellen können vollständig als abstrakte Klassen betrachtet werden. Im Vergleich zu abstrakten Klassen (Methoden in abstrakten Klassen können nur einen Teil der Methode implementieren und der andere Teil bleibt als Signatur definiert) kann die Vererbung jedoch nur eine einzelne Basisklasse, aber mehrere Schnittstellen erben. Schnittstellen (multiple Mixed) können als Alternative zur Mehrfachvererbung angesehen werden.
Der ECMA-262-3-Standard definiert weder das Konzept der „Schnittstelle“ noch das Konzept der „abstrakten Klasse“. Als Nachahmung ist es jedoch möglich, ein Objekt mit einer „leeren“ Methode zu implementieren (oder eine Ausnahme, die in eine leere Methode geworfen wird, um dem Entwickler mitzuteilen, dass diese Methode implementiert werden muss).
Objektkombination
Objektzusammensetzung ist auch eine der dynamischen Code-Wiederverwendungstechnologien. Die Objektkomposition unterscheidet sich von der hochflexiblen Vererbung dadurch, dass sie einen dynamisch veränderbaren Delegaten implementiert. Und dies auch auf Basis in Auftrag gegebener Prototypen. Zusätzlich zu dynamisch veränderbaren Prototypen kann das Objekt Objekte für Delegaten aggregieren (als Ergebnis eine Kombination erstellen – eine Aggregation) und außerdem Nachrichten an Objekte senden, die an den Delegaten delegieren. Dies kann mit mehr als zwei Delegaten durchgeführt werden, da es sich aufgrund seiner dynamischen Natur zur Laufzeit ändern kann.
Das bereits erwähnte __noSuchMethod__-Beispiel tut dies, aber wir zeigen auch, wie man Delegaten explizit verwendet:
Zum Beispiel:
Diese Objektbeziehung wird „has-a“ genannt und die Integration ist eine „is-a“-Beziehung.
Aufgrund der fehlenden expliziten Zusammensetzung (Flexibilität im Vergleich zur Vererbung) ist das Hinzufügen von Zwischencode auch in Ordnung.
AOP-Funktionen
Als aspektorientierte Funktion können Sie Funktionsdekoratoren verwenden. Die ECMA-262-3-Spezifikation definiert das Konzept der „Funktionsdekoratoren“ nicht klar (im Gegensatz zu Python, wo dieser Begriff offiziell definiert ist). Allerdings können Funktionen mit funktionalen Parametern in bestimmten Aspekten dekoriert und aktiviert werden (durch Anwendung sogenannter Vorschläge):
Das einfachste Dekorationsbeispiel:
Fazit
In diesem Artikel haben wir die Einführung von OOP erläutert (ich hoffe, diese Informationen waren für Sie hilfreich) und im nächsten Kapitel werden wir mit der Implementierung von ECMAScript für die objektorientierte Programmierung fortfahren.