einführen
In diesem Artikel betrachten wir verschiedene Aspekte der objektorientierten Programmierung in ECMAScript (obwohl dieses Thema zuvor in vielen Artikeln diskutiert wurde). Wir werden diese Probleme mehr aus theoretischer Sicht betrachten. Insbesondere werden wir den Erstellungsalgorithmus des Objekts betrachten, wie die Beziehung zwischen Objekten (einschließlich grundlegender Beziehungen - Vererbung) auch in der Diskussion verfügbar ist (ich hoffe, dass einige der konzeptionellen Unklarheiten von OOP in JavaScript entfernt werden).
Original Englisch Text: http://dmitrysoshnikov.com/ecmascript/chapter-7-1-oop-general-theory/
Einführung, Paradigma und Gedanken
Vor der Durchführung der technischen Analyse in ECMascript ist es erforderlich, einige grundlegende Merkmale von OOP zu beherrschen und die Hauptkonzepte in der Einführung zu klären.
ECMAScript unterstützt verschiedene Programmiermethoden, einschließlich strukturierter, objektorientierter, funktionaler, imperatives usw., und in einigen Fällen unterstützt es auch eine Aspekt-orientierte Programmierung. In diesem Artikel werden jedoch die objektorientierte Programmierung erörtert, sodass wir die Definition der objektorientierten Programmierung in ECMAScript angeben:
ECMAScript ist eine objektorientierte Programmiersprache, die auf der Prototyp-Implementierung basiert.
Es gibt viele Unterschiede zwischen prototypbasiertem OOP und statischen klassenbasierten Methoden. Schauen wir uns ihre direkten detaillierten Unterschiede an.
Klassenbasierte und prototypbasierte
Beachten Sie, dass der wichtige Punkt im vorherigen Satz bereits darauf hingewiesen hat - er basiert ausschließlich auf statischen Klassen. Mit dem Wort "statisch" verstehen wir statische Objekte und statische Klassen, die stark typisiert sind (obwohl nicht erforderlich).
In dieser Situation betonen viele Forum -Dokumente, dass dies der Hauptgrund ist, warum sie sich gegen den Vergleich von "Klassen mit Prototypen" in JavaScript widersetzen. Obwohl ihre Implementierungsunterschiede (wie Python und Ruby basierend auf dynamischen Klassen) nicht zu dem Punkt sind (einige Bedingungen sind geschrieben, obwohl es einige Unterschiede in der Ideen gibt, ist JavaScript nicht so alternativ), aber ihre Opposition sind statische Klassen und dynamische Prototypen (Statik + Klassen VS. Dynamics + Prototypes). Um genau zu sein, kann eine statische Klasse (wie C ++, Java) und ihre Untergebenen und Methodendefinitionsmechanismen uns den genauen Unterschied zwischen IT und Prototyp -Implementierung erkennen lassen.
Aber lass uns sie nacheinander auflisten. Betrachten wir die allgemeinen Prinzipien und Hauptkonzepte dieser Paradigmen.
Basierend auf statischen Klassen
In einem klassenbasierten Modell gibt es ein Konzept über Klassen und Instanzen. Instanzen von Klassen werden auch oft als Objekte oder Paradigmen bezeichnet.
Klassen und Objekte
Eine Klasse repräsentiert eine Abstraktion einer Instanz (d. H. Ein Objekt). Dies ist ein bisschen wie Mathematik, aber wir nennen es Typ oder Klassifizierung.
Zum Beispiel (die Beispiele hier und unten sind Pseudo-Code):
Die Codekopie lautet wie folgt:
C = Klasse {A, B, C} // Klasse C, einschließlich Merkmale A, B, C.
Die Eigenschaften der Instanz sind: Attribute (Objektbeschreibung) und Methoden (Objektaktivität). Die Merkmale selbst können auch als Objekte angesehen werden: Das heißt, ob das Attribut beschreibbar, konfigurierbar, einsetzbar (Getter/Setter) usw. Daher speichern Objekte den Zustand (d. H. Die spezifischen Werte aller in einer Klasse beschriebenen Attributen) und Klassen definieren strenginvariante Strukturen (Eigenschaften) und streng ein Invariantenverhalten (Methoden) für ihre Instanzen.
Die Codekopie lautet wie folgt:
C = Klasse {a, b, c, method1, method2}
c1 = {a: 10, b: 20, c: 30} // Klasse C ist eine Instanz: Objekt с1
C2 = {A: 50, B: 60, C: 70} // Klasse C ist eine Instanz: Objekt с2, die einen eigenen Zustand hat (dh Attributwert)
Hierarchische Erbe
Um die Wiederverwendung von Code zu verbessern, können Klassen von einem auf ein anderes erweitert werden, wodurch zusätzliche Informationen hinzugefügt werden. Dieser Mechanismus wird (hierarchische) Vererbung bezeichnet.
Die Codekopie lautet wie folgt:
D = Klasse erweitert C = {d, e} // {a, b, c, d, e}
d1 = {a: 10, b: 20, c: 30, d: 40, e: 50}
Wenn Sie die Partei auf der Instanz der Klasse anrufen, sucht die einheimische Klasse jetzt normalerweise nach der Methode. Wenn es nicht gefunden wird, gehen Sie in die übergeordnete Klasse, um direkt zu suchen. Wenn es nicht gefunden wird, gehen Sie in die übergeordnete Klasse der übergeordneten Klasse, um zu suchen (z. B. in der strengen Erbschaftskette). Wenn festgestellt wird, dass die Spitze des Vererbens nicht gefunden wurde, ist das Ergebnis: Das Objekt hat kein ähnliches Verhalten und kann das Ergebnis nicht erzielen.
Die Codekopie lautet wie folgt:
D1.Method1 () // D.Method1 (nein) -> C.Method1 (Ja)
D1.Method5 () // D.Method5 (NO) -> C.Method5 (NO) -> Kein Ergebnis
Im Vergleich zu Vererbungsmethoden, die nicht in eine Unterklasse kopiert werden, werden Eigenschaften immer in Unterklassen kompliziert. Wir können sehen, dass Unterklasse D aus der übergeordneten Klasse C: Attribute A, B, C kopiert werden und die Struktur von D {A, B, C, D, E}} ist. Die Methode {method1, method2} kopiert jedoch nicht die Vergangenheit, sondern erbt die Vergangenheit. Das heißt, wenn eine sehr tiefe Klasse einige Eigenschaften hat, die Objekte überhaupt nicht benötigen, hat die Unterklasse auch diese Eigenschaften.
Schlüsselkonzepte basierend auf Klassen
Daher haben wir die folgenden Schlüsselkonzepte:
1. Vor dem Erstellen eines Objekts muss die Klasse deklariert werden. Erstens ist es notwendig, seine Klasse zu definieren.
2. Daher wird das Objekt aus einer Klasse erstellt, die in seine eigene "piktografische und Ähnlichkeit" (Struktur und Verhalten) abstrahiert wurde
3. Die Methode wird durch eine strenge, direkte und unveränderliche Vererbungskette behandelt.
4. Die Unterklasse enthält alle Attribute in der Vererbungskette (auch wenn einige der Attribute von der Unterklasse nicht erforderlich sind);
5. Erstellen Sie eine Klasseninstanz. Die Klasse kann nicht (wegen eines statischen Modells) die Eigenschaften (Attribute oder Methoden) ihrer Instanz ändern;
6. Instanzen (wegen statischer statischer Modelle) können zusätzliche Verhaltensweisen oder Attribute außer den in der entsprechenden Klasse deklarierten Verhaltensweisen und Attributen zur Instanz haben.
Lassen Sie uns sehen, wie das OOP-Modell in JavaScript ersetzt wird.
Basierend auf Prototyp
Das grundlegende Konzept sind hier dynamische, veränderliche Objekte. Transformation (vollständige Konvertierung, einschließlich nicht nur Werte, sondern auch Merkmale) hängt direkt mit der dynamischen Sprache zusammen. Objekte wie die folgenden können alle ihre Eigenschaften (Eigenschaften, Methoden) ohne Klassen unabhängig speichern.
Die Codekopie lautet wie folgt:
Object = {A: 10, B: 20, C: 30, Methode: fn};
Objekt.a; // 10
Objekt.C; // 30
Object.Method ();
Darüber hinaus können sie aufgrund der Dynamik ihre eigenen Funktionen leicht ändern (hinzufügen, löschen, ändern):
Die Codekopie lautet wie folgt:
Object.Method5 = function () {...}; // eine neue Methode hinzufügen
Objekt.D = 40; // neues Attribut "D" hinzufügen
Objekt löschen.C; // Attribut "с" löschen
Objekt.A = 100; // das Attribut "а" ändern
// Das Ergebnis ist: Objekt: {a: 100, b: 20, d: 40, method: fn, method5: fn};
Das heißt, wenn zugewiesen wird, wird es erstellt, wenn einige Funktionen nicht vorhanden sind, und die Zuordnung wird damit initialisiert, und wenn sie existiert, wird sie nur aktualisiert.
In diesem Fall wird die Wiederverwendung von Code nicht durch Erweiterung der Klasse implementiert (beachten Sie, dass die Klasse nicht geändert werden kann, da es hier kein Konzept der Klasse gibt), sondern durch Prototypen implementiert wird.
Ein Prototyp ist ein Objekt, das als primitive Kopie anderer Objekte verwendet wird oder wenn einige Objekte nicht ihre eigenen notwendigen Eigenschaften haben, kann der Prototyp als Delegierter für diese Objekte verwendet werden und als Hilfsobjekt fungieren.
Basierend auf Delegation
Jedes Objekt kann als Prototypobjekt für ein anderes Objekt verwendet werden, da das Objekt seine Prototyp -Dynamik zur Laufzeit problemlos ändern kann.
Beachten Sie, dass wir derzeit eher eine Einführung als eine konkrete Implementierung in Betracht ziehen, und wenn wir konkrete Implementierung in ECMascript diskutieren, werden wir einige ihrer eigenen Merkmale sehen.
Beispiel (Pseudocode):
Die Codekopie lautet wie folgt:
x = {a: 10, b: 20};
y = {a: 40, c: 50};
y. [[Prototyp]] = x; // x ist der Prototyp von y
ya; // 40, seine eigenen Eigenschaften
yc; // 50 ist es auch sein eigenes Merkmal
yb; // 20 Erhalten Sie aus dem Prototyp: YB (nein) -> y. [[Prototyp]]. B (Ja): 20
ya löschen; // Löschen Sie Ihr eigenes "A"
ya; // 10 Holen Sie es aus dem Prototyp
z = {a: 100, e: 50}
y. [[Prototyp]] = Z; // den Prototyp von y nach z ändern
ya; // 100 erhalten Sie vom Prototyp z
ye // 50, auch aus Prototyp z erhalten
ZQ = 200 // dem Prototyp ein neues Attribut hinzufügen
YQ // Änderung gilt auch für y
Dieses Beispiel zeigt die wichtigen Funktionen und Mechanismen des Prototyps als Hilfsobjektattribute, genau wie Sie Ihre eigenen Attribute benötigen. Im Vergleich zu Ihren eigenen Attributen sind diese Attribute Delegierte Attribute. Dieser Mechanismus wird als Delegat bezeichnet, und das darauf basierende Prototypmodell ist ein Delegierprototyp (oder ein delegatbasiertes Prototyp). Der Referenzmechanismus wird als Senden von Informationen an ein Objekt bezeichnet. Wenn das Objekt keine Antwort erhält, wird es an den Prototyp delegiert, um es zu finden (so dass es versucht, auf die Nachricht zu antworten).
Die Wiederverwendung von Code in diesem Fall wird als delegatbasiertes Vererbung oder prototypbasiertes Vererbung bezeichnet. Da jedes Objekt als Prototyp angesehen werden kann, kann der Prototyp auch einen eigenen Prototyp haben. Diese Prototypen sind miteinander verbunden, um eine sogenannte Prototypkette zu bilden. Ketten sind auch in statischen Klassen hierarchisch, können aber leicht neu angeordnet werden, die Hierarchien und Strukturen ändern.
Die Codekopie lautet wie folgt:
x = {a: 10}
y = {b: 20}
y. [[Prototyp]] = x
z = {c: 30}
z. [[Prototyp]] = y
Za // 10
// Za in der Prototyp -Kette gefunden:
// Za (nein) ->
// z. [[Prototyp]]. A (no) ->
// z. [[Prototyp]]. [[Prototyp]]. A (Ja): 10
Wenn ein Objekt und seine Prototypkette nicht auf die Senden von Nachrichten reagieren können, kann das Objekt das entsprechende Systemsignal aktivieren, das von anderen Delegierten in der Prototypkette verarbeitet werden kann.
Dieses Systemsignal ist in vielen Implementierungen verfügbar, einschließlich von Systemen basierend auf dynamischen Klassen: #doesNotund -Verstehen in SmallTalk, method_missing in Ruby; __getAttr__ in Python, __call in PHP; und __nosuchmethod__ -Implementierung in ECMascript usw. etc.
Beispiel (Spiderermonkeys ECMAScript -Implementierung):
Die Codekopie lautet wie folgt:
var Object = {
// Fangen Sie das Systemsignal an, das nicht auf Nachrichten reagieren kann
__nosuchmethod__: Funktion (Name, Args) {
alarm (name, args]);
if (name == 'test') {
return '.Test () Methode wird behandelt';
}
Rückgabedelegierter [Name] .Apply (this, args);
}
};
var delegate = {
Quadrat: Funktion (a) {
zurück ein * a;
}
};
alert (Object.Square (10)); // 100
Alert (Object.Test ()); // .Test () Methode wird behandelt
Das heißt, basierend auf der Implementierung statischer Klassen ist die Schlussfolgerung, dass das aktuelle Objekt nicht über die erforderlichen Merkmale verfügt. Wenn Sie jedoch versuchen, es von der Prototyp -Kette zu erhalten, erhalten Sie möglicherweise immer noch das Ergebnis, oder das Objekt hat das Charakteristik nach einer Reihe von Änderungen.
In Bezug auf ECMAScript ist die spezifische Implementierung: Verwendung eines delegierten Prototyps. Wie wir jedoch aus Spezifikationen und Implementierungen erkennen werden, haben sie auch ihre eigenen Merkmale.
Geheimhaltendes Modell
Ehrlich gesagt ist es notwendig, etwas über eine andere Situation zu sagen (nicht so bald wie möglich in ECMascript verwendet): Diese Situation, in der der Prototyp von anderen Objekten bis hin zum ursprünglichen Ersatz nativer Objekte komplex ist. In diesem Fall ist die Wiederverwendung von Code eine echte Kopie (Klon) eines Objekts und eher ein Delegierter während der Objekterstellungstufe. Dieser Prototyp wird als übereinstimmender Prototyp bezeichnet. Das Kopieren der Eigenschaften aller Prototypen eines Objekts kann seine Eigenschaften und Methoden weiter vollständig ändern. Als Prototyp können Sie sich auch selbst ändern (in einem auf Delegaten basierenden Modell ändert diese Änderung das Verhalten vorhandener Objekte nicht, sondern verändert seine Prototyp-Eigenschaften). Der Vorteil dieser Methode besteht darin, dass sie den Zeitpunkt der Planung und Delegation verkürzen kann, während der Nachteil darin besteht, dass der Speicher verwendet wird.
Ententyp
Zurück zum dynamisch schwachen Typ-Änderungsobjekt im Vergleich zum statischen klassenbasierten Modell muss prüfen, ob es diese Dinge tun kann und welcher Typ (Klasse) das Objekt hat, aber ob es sich um die entsprechende Nachricht bezieht (d. H. Ob es nach Überprüfung erforderlich ist, ist erforderlich).
Zum Beispiel:
Die Codekopie lautet wie folgt:
// in einem statischen Modell
if (Objektinstanz von Someclass) {
// Einige Verhaltensweisen laufen
}
// in der dynamischen Implementierung
// Es spielt keine Rolle, welche Art des Objekts zu diesem Zeitpunkt ist
// Weil Mutationen, Typen und Eigenschaften frei geändert werden können.
// ob wichtige Objekte auf Testmeldungen antworten können
if (isFunction (Object.test)) // ecmascript
wenn object.respect_to? (: test) // Ruby
Wenn Hasattr (Objekt, 'Test'): // Python
Dies wird als Dock -Typ bezeichnet. Das heißt, Objekte können durch ihre eigenen Eigenschaften beim Überprüfen und nicht durch den Ort von Objekten in der Hierarchie identifiziert werden oder gehören zu einem bestimmten Typ.
Schlüsselkonzepte basierend auf Prototypen
Schauen wir uns die Hauptmerkmale dieser Methode an:
1. Das Grundkonzept ist das Objekt
2. Objekte sind vollständig dynamisch und veränderlich (theoretisch kann sie vollständig von einem Typ zu einem anderen transformiert werden)
3. Objekte haben keine strengen Klassen, die ihre eigene Struktur und ihr eigenes Verhalten beschreiben, und Objekte benötigen keine Klassen.
4. Objekte haben keine Klassenklassen, können aber Prototypen haben. Wenn sie nicht auf Nachrichten antworten können, können sie an Prototypen delegieren.
5. Der Prototyp des Objekts kann jederzeit während der Laufzeit geändert werden.
6. In einem delegierten Modell wirkt sich die Änderung der Eigenschaften des Prototyps alle Objekte im Zusammenhang mit dem Prototyp aus.
7. Im übereinstimmenden Prototypmodell ist der Prototyp die ursprüngliche Kopie, die aus anderen Objekten kloniert ist und weiter zu einem völlig unabhängigen Kopieroriginal wird. Die Transformation der Eigenschaften des Prototyps wirkt sich nicht auf das daraus geklonte Objekt aus.
8. Wenn die Nachricht nicht beantwortet werden kann, kann ihr Anrufer zusätzliche Maßnahmen ergreifen (z. B. ändern Sie die Planung).
9. Objektversagen kann nicht durch ihre Ebene und in welcher Klasse sie gehören, sondern durch die aktuellen Eigenschaften bestimmt werden.
Es gibt jedoch auch ein anderes Modell, das wir auch berücksichtigen sollten.
Basierend auf dynamischen Klassen
Wir glauben, dass der im obige Beispiel gezeigte Unterschied "Klasse vs Prototyp" in diesem dynamischen klassenbasierten Modell nicht so wichtig ist (insbesondere wenn die Prototypkette unverändert ist, ist es immer noch erforderlich, eine statische Klasse für eine genauere Unterscheidung zu berücksichtigen). Beispielsweise kann es auch in Python oder Ruby (oder in ähnlichen Sprachen) verwendet werden. Diese Sprachen verwenden alle dynamische klassenbasierte Paradigmen. In einigen Aspekten sehen wir jedoch bestimmte Funktionen, die basierend auf Prototypen implementiert sind.
Im folgenden Beispiel können wir feststellen, dass wir nur auf dem Delegiertenprototyp eine Klasse (Prototype) amplifizieren können, wodurch alle Objekte im Zusammenhang mit dieser Klasse betroffen sind, können wir auch die Klasse dieses Objekts zur Laufzeit dynamisch ändern (Bereitstellung eines neuen Objekts für den Delegieren) usw.
Die Codekopie lautet wie folgt:
# Python
Klasse A (Objekt):
def __init __ (self, a):
self.a = a
Def Square (Selbst):
return self.a * self.a
a = a (10) # Erstellen Sie eine Instanz
Druck (AA) # 10
AB = 20 # Bieten Sie eine neue Eigenschaft für die Klasse an
Drucken (AB) # 20 Sie können in der "A" -Instanz darauf zugreifen
a = 30 # Erstellen Sie eine eigene Eigenschaft
Druck (ab) # 30
Del Ab # Löschen Sie seine eigenen Attribute
Drucken (AB) # 20 - Holen Sie es erneut aus der Klasse (Prototyp)
# Wie ein prototypbasiertes Modell
# Kann den Prototyp eines Objekts zur Laufzeit ändern
Klasse B (Objekt): # leere Klasse B
passieren
b = b () # b Instanz
b .__ class__ = a # dynamisch ändern (Prototyp)
Ba = 10 # Neues Attribut erstellen
Print (b.Square ()) # 100 - Die Methode der Klasse A ist zu diesem Zeitpunkt verfügbar
# Kann Referenzen in gelöschten Klassen anzeigen
del a
Del b
# Das Objekt hat jedoch immer noch implizite Referenzen, und diese Methoden sind noch verfügbar
print (b.square ()) # 100
# Aber Sie können die Klasse zu diesem Zeitpunkt nicht ändern
# Dies ist das Merkmal der Implementierung
b .__ class__ = dict # fehler
Die Implementierung in Ruby ist ähnlich: Sie verwendet auch vollständig dynamische Klassen (in der aktuellen Version von Python können im Vergleich zu Ruby und ECMascript die Klassen (Prototypen) nicht durchgeführt werden). Wir können die Eigenschaften eines Objekts vollständig ändern (oder die Klassen hinzufügen) (addieren Sie die Methoden/Attribute in die Klasse, und das Klassen wird nicht dynamisch verändert.
Dieser Artikel ist jedoch nicht speziell für Python und Ruby, daher werden wir nicht viel sagen, lasst uns weiterhin Ecmascript selbst diskutieren.
Vorher müssen wir uns jedoch den "synthetischen Zucker" ansehen, der in einigen OOPS zu finden ist, da viele frühere Artikel über JavaScript diese Probleme häufig behandeln.
Der einzige falsche Satz, der in diesem Abschnitt festgestellt werden muss, lautet: "JavaScript ist keine Klasse, es hat einen Prototyp und kann eine Klasse ersetzen." Es ist sehr notwendig zu wissen, dass nicht alle klassenbasierten Implementierungen völlig unterschiedlich sind. Auch wenn wir sagen können, dass "JavaScript anders ist", muss auch (neben dem Konzept der "Klassen") berücksichtigt werden, dass es andere verwandte Merkmale gibt.
Andere Merkmale verschiedener OOP -Implementierungen
In diesem Abschnitt stellen wir kurz andere Funktionen und verschiedene OOP -Implementierungen zur Wiederverwendung von Code vor, einschließlich OOP -Implementierungen in ECMAScript. Der Grund dafür ist, dass das vorherige Erscheinen der OOP -Implementierung in JavaScript einige gewohnheitsmäßige Denkbeschränkungen aufweist. Die einzige Hauptanforderung ist, dass es technisch und ideologisch nachgewiesen werden sollte. Es kann nicht gesagt werden, dass Sie die Syntaxzuckerfunktion in anderen OOP -Implementierungen nicht entdeckt haben, und Sie haben keine Ahnung, dass JavaScript keine reine OOP -Sprache ist, was falsch ist.
Polymorph
In ECMascript gibt es mehrere Polymorphismen, in denen Objekte bedeuten.
Beispielsweise kann eine Funktion auf verschiedene Objekte angewendet werden, genau wie die Eigenschaften eines nativen Objekts (da dieser Wert beim Eingeben des Ausführungskontexts bestimmt wird):
Die Codekopie lautet wie folgt:
Funktionstest () {
Alert ([this.a, this.b]);
}
test.call ({a: 10, b: 20}); // 10, 20
test.call ({a: 100, b: 200}); // 100, 200
var a = 1;
var b = 2;
prüfen(); // 1, 2
Es gibt jedoch Ausnahmen: Datum.
Die Codekopie lautet wie folgt:
alert (date.prototype.getTime.call (neues Datum ())); // Zeit
alert (date.prototype.getTime.call (neuer String (''))); // TypeRror
Der sogenannte Parameterpolymorphismus beim Definieren einer Funktion entspricht allen Datentypen, akzeptiert jedoch nur polymorphe Parameter (wie die Sortiermethode für die Sortierung eines Arrays und seine Parameter - die Sortierfunktion des Polymorphismus). Übrigens kann das obige Beispiel auch als Parameterpolymorphismus betrachtet werden.
Die Methode im Prototyp kann als leer definiert werden, und alle erstellten Objekte sollten neu definiert (implementiert) (d. H. "Eine Schnittstelle (Signatur), mehrere Implementierungen").
Der Polymorphismus hängt mit dem oben erwähnten Ententyp zusammen: d. H. Der Typ des Objekts und seine Position in der Hierarchie sind nicht so wichtig, aber es kann leicht akzeptiert werden, wenn er alle erforderlichen Merkmale aufweist (d. H. Die allgemeine Schnittstelle ist wichtig und die Implementierung kann variiert werden).
Paket
Es gibt oft falsche Ansichten über die Kapselung. In diesem Abschnitt werden wir einige syntaktische Zucker in OOP -Implementierungen erörtern - dh bekannte Modifikatoren: In diesem Fall werden wir einige OOP -Implementierungen bequemer "Zucker" - bekannte Modifikatoren: privat, geschützt und öffentlich (oder Zugriffsniveau oder Zugriffsmodifikatoren der Objekte) besprechen.
Hier möchte ich Sie an den Hauptzweck der Kapselung erinnern: Die Kapselung ist eine abstrakte Ergänzung, anstatt einen versteckten "böswilligen Hacker" auszuwählen, der etwas direkt in Ihre Klasse schreibt.
Dies ist ein großer Fehler: Verwenden Sie versteckt, um sich zu verbergen.
Die Zugangsniveaus (privat, geschützt und öffentlich) wurden für die Programmen in vielen objektorientierten (wirklich sehr bequemen Syntaxzucker) implementiert und beschreiben und bauen das System abstrakter.
Diese sind in einigen Implementierungen zu sehen (wie bereits erwähnte Python und Ruby). Einerseits (in Python) sind diese __Privierten Eigenschaften (über die Unterstrichespezifikation) von außen nicht zugänglich. Python hingegen kann von außen durch spezielle Regeln (_className____field_name) zugegriffen werden.
Die Codekopie lautet wie folgt:
Klasse A (Objekt):
def __init __ (selbst):
self.public = 10
self .__ privat = 20
def get_private (self):
Rückkehr selbst .__ Privat
# draußen:
a = a () # Beispiel von a
print (a.public) # ok, 30
print (a.get_private ()) # OK, 20
drucken (a .__ privat) # fehlgeschlagen, da es nur in a verfügbar sein kann
# Aber in Python können auf besondere Regeln zugegriffen werden
print (a._a__private) # ok, 20
In Ruby: Einerseits kann es die Eigenschaften von privaten und geschützten Merkmalen definieren. Andererseits gibt es auch spezielle Methoden (z.
Die Codekopie lautet wie folgt:
Klasse a
Def initialisieren
@A = 10
Ende
Def public_method
privat_method (20)
Ende
Privat
Def private_method (b)
return @A + b
Ende
Ende
A = A.New # Neue Instanz
a.public_method # ok, 30
AA # fehlgeschlagen, @A - ist eine private Instanzvariable
# "private_method" ist privat und kann nur in Klasse A zugegriffen werden
A.Private_Method # Fehler
# Es gibt jedoch einen speziellen Metadaten -Methodennamen, mit dem die Daten abgerufen werden können
A.Send (: privat_method, 20) # ok, 30
A.instance_variable_get (:@a) # ok, 10
Der Hauptgrund sind die Kapselung (beachten Sie, dass ich keine "versteckten" Daten verwende), die der Programmierer erhalten möchte. Wenn diese Daten in irgendeiner Weise falsch geändert werden oder Fehler vorliegen, ist die gesamte Verantwortung der Programmierer, aber nicht einfach "Sprüchen" oder "bestimmte Felder beiläufig ändern". Wenn dies jedoch häufig vorkommt, ist es eine schlechte Programmiergewohnheit und -stil, da es normalerweise eine gemeinsame API ist, mit Objekten zu "sprechen".
Wiederholen, der grundlegende Zweck der Kapselung besteht darin, sie vom Benutzer von Hilfsdaten abzuziehen, anstatt zu verhindern, dass Hacker Daten verstecken. Noch ernsthafter, die Kapselung besteht nicht darin, privat zu verwenden, um Daten zu ändern, um den Zweck der Software -Sicherheit zu erreichen.
Einkapselung von Helferobjekten (lokal). Wir bieten Machbarkeit für Verhaltensänderungen öffentlicher Schnittstellen mit minimalen Kosten, Lokalisierung und prädiktiven Änderungen, was genau der Zweck der Einkapselung ist.
Darüber hinaus besteht der wichtige Zweck der Setter -Methode darin, komplexe Berechnungen abstrahieren. Zum Beispiel ist Element.innerHtml Setter - Abstract Anweisung - "Das HTML in diesem Element ist jetzt wie folgt", und die Setterfunktion im Innerhtml -Attribut ist schwer zu berechnen und zu überprüfen. In diesem Fall beinhaltet das Problem hauptsächlich eine Abstraktion, aber eine Kapselung kann auftreten.
Das Konzept der Kapselung hängt nicht nur mit OOP zusammen. Zum Beispiel könnte es eine einfache Funktion sein, die alle Arten von Berechnungen so zusammenfasst, dass sie abstrakt ist (keine Notwendigkeit, den Benutzer mitzuteilen, beispielsweise wie die Funktion mathematisch (......) implementiert ist, nennt der Benutzer es einfach). Es ist eine Verkapselung, beachten Sie, dass ich nicht "privat, geschützt und öffentlich" gesagt habe.
Die aktuelle Version der Spezifikation der ECMAScript definiert keine privaten, geschützten und öffentlichen Modifikatoren.
In der Praxis ist es jedoch möglich, etwas zu sehen, das "JS -Kapselung nachahmt". Im Allgemeinen ist der Zweck dieses Kontextes zu verwenden (in der Regel der Konstruktor selbst). Leider kann häufig die Implementierung dieser "Nachahmung" durchgeführt werden, und Programmierer können pseudo-absolut nicht abstrakte Entitäten produzieren, um die "Getter/Setter-Methode" festzulegen (ich sage es erneut, es ist falsch):
Die Codekopie lautet wie folgt:
Funktion a () {
var _a; // "privat" a
this.geta = function _getA () {
return _a;
};
this.seta = function _seta (a) {
_a = a;
};
}
var a = new a ();
A.Seta (10);
alert (a._a); // undefiniert, "privat"
Alert (A.geta ()); // 10
Daher versteht jeder, dass für jedes erstellte Objekt auch die Geta/Seta -Methode erstellt wird, was auch der Grund für die Speichererhöhung ist (im Vergleich zur Prototypdefinition). Obwohl das Objekt theoretisch im ersten Fall optimiert werden kann.
Darüber hinaus erwähnen einige JavaScript -Artikel häufig das Konzept der "privaten Methode". Hinweis: ECMA-262-3 Standard definiert kein Konzept der "privaten Methode".
In einigen Fällen kann es jedoch im Konstruktor erstellt werden, da JS eine ideologische Sprache ist - Objekte sind vollständig veränderlich und haben einzigartige Eigenschaften (unter bestimmten Bedingungen im Konstruktor können einige Objekte zusätzliche Methoden erhalten, andere nicht).
In JavaScript wird in JavaScript, wenn die Kapselung immer noch falsch interpretiert wird, um böswillige Hacker daran zu hindern, bestimmte Werte automatisch anstelle der Setter-Methode zu schreiben, die sogenannten "versteckten" und "privaten" nicht "versteckt". Einige Implementierungen können Werte für die relevante Bereichskette (und alle entsprechenden variablen Objekte) erhalten, indem sie den Kontext zur EV -Funktion aufrufen (die auf Spidermonkey 1.7 getestet werden kann).
Die Codekopie lautet wie folgt:
eval ('_ a = 100', a.geta); // oder A.Seta, weil "_a" Methoden auf [[Scope]]
A.geta (); // 100
Alternativ können Sie in der Implementierung einen direkten Zugriff auf das aktive Objekt (z. B. Rhino) ermöglichen, indem Sie auf die entsprechenden Eigenschaften des Objekts zugreifen, kann der Wert der internen Variablen geändert werden:
Die Codekopie lautet wie folgt:
// Nashorn
var foo = (function () {
var x = 10; // "Privat"
return function () {
print (x);
};
}) ();
foo (); // 10
foo .__ Eltern __. x = 20;
foo (); // 20
Manchmal wird in JavaScript der Zweck von "privaten" und "geschützten" Daten durch Unterstrichvariablen erreicht (im Vergleich zu Python ist dies nur eine Namensspezifikation):
var _mypivatedata = 'testString';
Es wird oft verwendet, um den Ausführungskontext mit Klammern einzuschließen, aber für echte Hilfsdaten hat es keine direkte Assoziation mit Objekten, und es ist nur zweckmäßig, von externen APIs abzuwehren:
Die Codekopie lautet wie folgt:
(function () {
// den Kontext initialisieren
}) ();
Multiple Vererbung
Multi-Inheritance ist ein sehr bequemer syntaktischer Zucker für Verbesserungen zur Wiederverwendung von Code (wenn wir jeweils eine Klasse erben können, warum können wir dann nicht jeweils 10 erben?). Aufgrund einiger Mängel in mehreren Erbschaften ist es jedoch bei der Implementierung nicht beliebt geworden.
ECMAScript unterstützt nicht die multiple Vererbung (d. H. Nur ein Objekt kann als direkter Prototyp verwendet werden), obwohl die selbstprogrammierenden Sprachen seiner Vorfahren solche Funktionen haben. In einigen Implementierungen (z. B. Spidermonkey) kann jedoch __nosuchmethod__ verwendet werden, um die Planung zu verwalten und zu delegieren, um Prototyp -Ketten zu ersetzen.
Mixins
Mixins ist eine bequeme Möglichkeit, den Code wiederzuverwenden. Mixins wurde als Ersatz für mehrere Erbschaften empfohlen. Diese unabhängigen Elemente können alle mit jedem Objekt gemischt werden, um ihre Funktionalität zu erweitern (so können Objekte auch mehrere Mixins mischen). Die ECMA-262-3-Spezifikation definiert das Konzept von "Mixins" nicht, aber nach der Definition von Mixins und dem ECMAScript hat dynamische mutable Objekte, sodass es kein Hindernis für die einfache Erweiterung von Merkmalen mit Mixins gibt.
Typische Beispiele:
Die Codekopie lautet wie folgt:
// Helfer für die Augmentation
Object.extend = Funktion (Ziel, Quelle) {
für (Eigenschaft in der Quelle) if (source.hasownProperty (Eigenschaft)) {
Ziel [Eigenschaft] = Quelle [Eigenschaft];
}
Ziel zurückgeben;
};
var x = {a: 10, b: 20};
var y = {c: 30, d: 40};
Object.extend (x, y); // y in x mischen
ALERT ([XA, XB, XC, XD]); 10, 20, 30, 40
Bitte beachten Sie, dass ich diese Definitionen ("Mixin", "Mix") in Zitaten in ECMA-262-3 genommen habe. Es gibt kein solches Konzept in der Spezifikation, und es handelt sich nicht um eine Mischung, sondern um eine häufig verwendete Möglichkeit, Objekte durch neue Funktionen zu erweitern. (Das Konzept von Mixins in Ruby ist offiziell definiert. Mixin erstellt einen Verweis auf das Modul, anstatt einfach alle Attribute des Moduls auf ein anderes Modul zu kopieren - tatsächlich: Erstellen eines zusätzlichen Objekts (Prototyp) für den Delegierten).
Merkmale
Merkmale und Mixins haben ähnliche Konzepte, aber es haben viele Funktionen (per Definition, da Mixins angewendet werden können, sodass es keine Zustände enthalten kann, da es das Potenzial hat, Namenskonflikte zu verursachen). Laut ECMascript folgen Merkmale und Mischungen den gleichen Prinzipien, sodass die Spezifikation das Konzept der "Merkmale" nicht definiert.
Schnittstelle
Die in einigen OOPS implementierten Schnittstellen ähneln Mixins und Merkmalen. Im Vergleich zu Mixins und Merkmalen erzwingt eine Schnittstelle die Implementierungsklasse, um ihr Verhalten der Methodensignatur zu implementieren.
Schnittstellen können als abstrakte Klassen angesehen werden. Im Vergleich zu abstrakten Klassen (die Methoden in abstrakten Klassen können nur einen Teil von ihnen implementieren, und der andere Teil wird immer noch als Signaturen definiert) kann die Vererbung nur eine einzelne Basisklasse der Vererbung sein, kann jedoch mehrere Schnittstellen erben. Aus diesem Grund können Schnittstellen (gemischtes Mehrfach) als Alternative zu mehreren Vererbung angesehen werden.
Der ECMA-262-3-Standard definiert weder das Konzept der "Schnittstelle" noch das Konzept der "abstrakten Klasse". Als Nachahmung kann es jedoch durch ein Objekt implementiert werden, das "leere" Methode (oder eine Ausnahme, die in eine leere Methode geworfen wird, um dem Entwickler mitzuteilen, dass diese Methode implementiert werden muss).
Objektkombination
Die Objektkombination ist auch eine der dynamischen Code -Wiederverwendungstechniken. Objektkombinationen unterscheiden sich von einer hohen Flexibilitätsvererbung, die einen dynamischen und variablen Delegierten implementiert. Und dies basiert auch auf dem Hauptprototyp. Zusätzlich zu dynamischen veränderlichen Prototypen kann das Objekt ein delegiertes aggregiertes Objekt sein (eine Kombination als Ergebnis erstellen - die Aggregation) und Nachrichten weiter an das Objekt senden und an den Delegierten delegieren. Dies kann mehr als zwei Delegierte sein, da seine dynamische Natur feststellt, dass es zur Laufzeit geändert werden kann.
Das erwähnte Beispiel __nosuchmeth__ ist so, aber können wir zeigen, wie Delegierte explizit verwendet werden:
Zum Beispiel:
Die Codekopie lautet wie folgt:
var _delegate = {
foo: function () {
alert ('_ delegate.foo');
}
};
var aggregate = {
Delegierter: _delegate,
foo: function () {
zurück this.delegate.foo.call (this);
}
};
Aggregate.foo (); // delegate.foo
Aggregate.delegate = {
foo: function () {
alarm ('foo from New Delegate');
}
};
Aggregate.foo (); // foo vom Neu -Delegierten
Diese Objektbeziehung heißt "HAS-A", während die Integration die Beziehung von "IS-A" ist.
Aufgrund des Mangels an Anzeigekombinationen (Flexibilität im Vergleich zur Vererbung) ist es auch in Ordnung, Zwischencode hinzuzufügen.
AOP -Funktionen
Als Aspekt-orientierte Funktion können Funktionsdekoratoren verwendet werden. Die ECMA-262-3-Spezifikation hat kein klar definiertes Konzept von "Funktionsdekoratoren" (im Gegensatz zu Python ist das Wort offiziell in Python definiert). Funktionen mit funktionellen Parametern sind jedoch in gewisser Weise dekorativ und aktiviert (durch Anwendung sogenannter Vorschläge):
Das einfachste Beispiel eines Dekorateurs:
Die Codekopie lautet wie folgt:
Funktion CheckDeCorator (Originalfunktion) {
return function () {
if (foobar! = 'test') {
alert ('falscher Parameter');
false zurückgeben;
}
return originalFunction ();
};
}
Funktionstest () {
alert ('Testfunktion');
}
var testwithCheck = checkDecorator (Test);
var foobar = false;
prüfen(); // 'Testfunktion'
TestWithCheck (); // 'falscher Parameter'
foobar = 'test';
prüfen(); // 'Testfunktion'
TestWithCheck (); // 'Testfunktion'
abschließend
In diesem Artikel haben wir die Einführung in OOP aussortiert (ich hoffe, diese Informationen waren für Sie nützlich), und im nächsten Kapitel werden wir weiterhin ECMAScript in objektorientierter Programmierung implementieren.