Einführung in das Decorator-MusterDas Decorator-Muster, auch als Decorator-Muster bekannt, fügt dynamisch zusätzliche Verantwortlichkeiten hinzu, ohne das Objekt selbst zu ändern. Es ist eine Art Strukturmuster. Vorteile der Verwendung des Dekorationsmusters: Trennung der Kernaufgaben des Objekts von den zu dekorierenden Funktionen. Nicht-invasive Verhaltensänderung. Beispielsweise kann ein Mädchen mit durchschnittlichem Aussehen mithilfe der Beauty-Funktion auch umwerfend aussehen. Wenn Sie die zusätzlichen Dekorationsfunktionen gut nutzen können, das Gesicht verschmälern, die Augen vergrößern, die Haut glätten und ein Foto machen, werden Sie fantastisch aussehen. Nach dieser Reihe von aufgetragenen Dekorationen sind Sie immer noch Sie selbst, Ihr Aussehen bleibt dasselbe, aber Sie können vor der Kamera an Schönheit gewinnen. Wenn Sie möchten, können Sie auch verschiedene Dekorationsstile ausprobieren. Solange die Dekorationsfunktion gut gemacht ist, können Sie ein „Mr. Variety Star“ werden. Dies kann in Code ausgedrückt werden, indem jede Funktion in eine Klasse abstrahiert wird: // Mädchenklasse Mädchen { Gesichtswert() { console.log('Mein ursprüngliches Gesicht') } } Klasse ThinFace { Konstruktor (Mädchen) { dieses.Mädchen = Mädchen; } Gesichtswert() { dieses.Mädchen.Gesichtswert(); console.log('Gesichtsverschlankung aktivieren') } } Klasse IncreasingEyes { Konstruktor (Mädchen) { dieses.Mädchen = Mädchen; } Gesichtswert() { dieses.Mädchen.Gesichtswert(); console.log('Vergrößere deine Augen') } } lass Mädchen = neues Mädchen(); Mädchen = neues ThinFace(Mädchen); Mädchen = neue zunehmende Augenzahl (Mädchen); // Blende deine Augen aus, Mädchen.faceValue(); // Gemessen an der Leistung des Codes ist das Einbetten eines Objekts in ein anderes Objekt gleichbedeutend mit dem Umhüllen eines Objekts mit einem anderen Objekt zur Bildung einer Umhüllungskette. Nach dem Aufruf wird es an jedes Objekt entlang der Verpackungskette weitergegeben, sodass jedes Objekt die Möglichkeit hat, es zu verarbeiten. Diese Methode bietet große Flexibilität beim Hinzufügen und Entfernen von Dekorationsfunktionen. Wenn Sie den Mut haben, Ihr wahres Gesicht zu zeigen, entfernen Sie einfach die Verpackung zur Gesichtsverschlankung. Dies hat keine Auswirkungen auf andere Funktionen. Wenn Sie eine Hautglättung hinzufügen möchten, fügen Sie eine weitere Funktionsklasse hinzu und dekorieren Sie diese weiter. Dies hat keine Auswirkungen auf andere Funktionen und sie können gleichzeitig ausgeführt werden. Das Hinzufügen kleiner Funktionen zu JavaScript mithilfe von Klassen ist etwas umständlich. Der Vorteil von JavaScript besteht darin, dass es flexibel ist und mithilfe von Objekten dargestellt werden kann: lass Mädchen = { Gesichtswert() { console.log('Mein ursprüngliches Gesicht') } } Funktion thinFace() { console.log('Gesichtsverschlankung aktivieren') } Funktion IncreasingEyes() { console.log('Vergrößere deine Augen') } Mädchen.GesichtWert = Funktion(){ const originalFaveValue = girl.faceValue; // ursprüngliche Funktion return function() { originalFaveValue.call(Mädchen); thinFace.call(Mädchen); } }() Mädchen.GesichtWert = Funktion(){ const originalFaveValue = girl.faceValue; // ursprüngliche Funktion return function() { originalFaveValue.call(Mädchen); IncreasingEyes.call(Mädchen); } }() Mädchen.Gesichtswert(); Ohne den Originalcode zu ändern, wird zunächst die ursprüngliche Funktion beibehalten, dann neu geschrieben und die beibehaltene Funktion im neu geschriebenen Code aufgerufen. Verdeutlichen Sie das Prinzip des Decorator-Patterns anhand eines Bildes: Wie aus der Abbildung ersichtlich, wird durch die schichtweise Verpackung die Funktionalität des ursprünglichen Objektes gesteigert. Dekoratoren in TypeScriptDer Dekorator in TypeScript verwendet die Form @expression. Nachdem expression ausgewertet wurde, handelt es sich um eine Funktion, die zur Laufzeit aufgerufen wird, und die dekorierten Deklarationsinformationen werden als Parameter übergeben. Die Dekoratoren in der Javascript-Spezifikation befinden sich derzeit in der zweiten Ausschreibungsphase, was bedeutet, dass sie nicht direkt im nativen Code verwendet werden können und noch nicht von Browsern unterstützt werden. Die Dekoratorsyntax kann während der Kompilierungsphase mithilfe von Babel- oder TypeScript-Tools in einen im Browser ausführbaren Code umgewandelt werden. (Am Ende erfolgt eine Analyse des kompilierten Quellcodes) Im Folgenden wird hauptsächlich die Verwendung von Dekoratoren in TypeScript besprochen. Dekoratoren in TypeScript können an Klassendeklarationen, Methoden, Zugriffsmethoden (Getter/Setter), Eigenschaften und Parameter angehängt werden. Um die Unterstützung für Dekoratoren zu aktivieren, kompilieren Sie die Datei in der Befehlszeile: tsc --target ES5 --experimentalDecorators test.ts Konfigurationsdatei tsconfig.json { "Compileroptionen": { "Ziel": "ES5", "experimentalDecorators": wahr } } Einsatz von DekorateurenEin Dekorator ist eigentlich eine Funktion. Wenn Sie ihn verwenden, fügen Sie ihm das @-Symbol voran und schreiben Sie ihn vor die zu dekorierende Deklaration. Wenn mehrere Dekoratoren gleichzeitig auf dieselbe Deklaration einwirken, können Sie sie in einer Zeile oder in einer neuen Zeile schreiben: //Schreibe @test1 in eine neue Zeile @test2 Erklärung //Schreibe eine Zeile @test1 @test2 ... Erklärung Definieren Sie die Datei face.ts: Funktion thinFace() { console.log('Gesichtsverschlankung aktivieren') } @thinFace Klasse Mädchen { } In JS-Code kompilieren und die ThinFace-Funktion direkt zur Laufzeit aufrufen. Dieser Dekorator wirkt auf eine Klasse und wird als Klassendekorator bezeichnet. Wenn Sie mehrere Funktionen hinzufügen müssen, können Sie mehrere Dekoratoren miteinander kombinieren: Funktion thinFace() { console.log('Gesichtsverschlankung aktivieren') } Funktion IncreasingEyes() { console.log('Vergrößere deine Augen') } @thinFace @ErhöhteAugen Klasse Mädchen { } Wenn mehrere Dekoratoren zur Laufzeit miteinander kombiniert werden, beachten Sie bitte, dass die Aufrufreihenfolge von unten nach oben erfolgt, was genau das Gegenteil der Schreibreihenfolge ist. Die im Beispiel angegebenen Laufergebnisse sind:
Wenn Sie einer Klasse in einem Dekorator Attribute hinzufügen und diese in anderen Dekoratoren verwenden möchten, sollten Sie dies in den letzten Dekorator schreiben, da der letzte Dekorator zuerst aufgerufen wird. Dekorateur FabrikManchmal müssen Sie einige Parameter an den Dekorator übergeben, wozu Sie die Hilfe der Dekorator-Factory-Funktion benötigen. Die Dekorationsfabrikfunktion ist eigentlich eine Funktion höherer Ordnung, die nach dem Aufruf eine Funktion zurückgibt, und die zurückgegebene Funktion dient als Dekorationsfunktion. Funktion thinFace(Wert: Zeichenfolge){ console.log('1-Face-Ausdünnungs-Fabrikmethode') Rückgabefunktion(){ console.log(`4-Ich bin ein Gesichtsverschlankungsdekorateur, ich möchte mein Gesicht verschlanken ${value}`) } } Funktion IncreasingEyes(Wert: Zeichenfolge) { console.log('2-Factory-Methode zum Vergrößern der Augen') Rückgabefunktion(){ console.log(`3-Ich bin ein Dekorateur, der die Augen vergrößert, und ich brauche ${value}`) } } @thinFace('50%') @IncreasingEyes('Doppelt') Klasse Mädchen { } Die Factory-Funktion wird nach dem @-Symbol aufgerufen und von oben nach unten ausgeführt, um die Dekorator-Funktion zu erhalten. Die Ausführungsreihenfolge der Dekorationsfunktionen ist weiterhin von unten nach oben. Das Ergebnis des Ausführens ist:
Um es zusammenzufassen:
KlassendekorateureDekoratoren, die auf Klassendeklarationen einwirken, geben uns die Möglichkeit, die Klasse zu ändern. Wenn die Dekoratorfunktion ausgeführt wird, wird der Klassenkonstruktor an die Dekoratorfunktion übergeben. Funktion classDecorator(Wert: Zeichenfolge){ return Funktion (Konstruktor) { console.log('Einen Konstruktor empfangen') } } Funktion thinFace(Konstruktor){ Konstruktor.prototype.thinFaceFeature = Funktion() { console.log('Funktion zur Gesichtsverdünnung') } } @thinFace @classDecorator('Klassendekorateur') Klasse Mädchen {} lass g = neues Mädchen(); g.thinFaceFeature(); // 'Dünnes Gesichtsmerkmal' Im obigen Beispiel können Sie nach dem Abrufen des Übertragungskonstruktors dem Konstruktor-Prototyp neue Methoden hinzufügen und sogar andere Klassen erben. Methodendekoratoren Es gibt zwei Arten von Methoden, die auf eine Klasse einwirken: statische Methoden und Prototypmethoden. Beim Einwirken auf eine statische Methode erhält die Dekoratorfunktion den Klassenkonstruktor; beim Einwirken auf eine Prototypmethode erhält die Dekoratorfunktion das Prototypobjekt. Funktion methodDecorator(Wert: Zeichenfolge, Mädchen){ return function(Prototyp, Schlüssel, Deskriptor){ console.log('Prototypobjekt empfangen, dekorierten Eigenschaftsnamen, Eigenschaftsdeskriptor', Girl.prototype === Prototyp) } } Funktion thinFace(Prototyp, Schlüssel, Deskriptor){ // Behalten Sie die ursprüngliche Methodenlogik bei. let originalMethod = descriptor.value; // Neu schreiben, Logik hinzufügen und die ursprüngliche Logik ausführen descriptor.value = function(){ originalMethod.call(this); // Beachten Sie, dass dies auf console.log('Gesichtsverschlankungsmodus aktivieren') verweist. } } Klasse Mädchen { @thinFace @methodDecorator('Methodendekorateur', Mädchen) Gesichtswert(){ console.log('Mein ursprüngliches Erscheinungsbild') } } sei g = neues Mädchen(); g.faceValue(); Wie aus dem Code ersichtlich, erhält die Dekorationsfunktion drei Parameter: Prototypobjekt, Methodenname und Beschreibungsobjekt. Wenn Sie mit dem beschriebenen Objekt nicht vertraut sind, können Sie hierauf zurückgreifen; Um die Funktionalität zu erweitern, können Sie die ursprüngliche Funktion beibehalten und den Wert des Beschreibungsobjekts in eine andere Funktion umschreiben. Bei Verwendung der Zugriffsmethode g.faceValue() wird auf den Wert zugegriffen, der dem Beschreibungsobjektwert entspricht. Fügen Sie der neu geschriebenen Funktion Logik hinzu und führen Sie die beibehaltene ursprüngliche Funktion aus. Beachten Sie, dass die ursprüngliche Funktion „Call“ oder „Apply“ verwenden muss, um auf das Prototypobjekt zu verweisen. ImmobiliendekorateureEs wirkt auf die in der Klasse definierten Attribute. Diese Attribute sind keine Attribute des Prototyps, sondern Attribute des Instanzobjekts, das durch Instanziieren der Klasse erhalten wird. Der Dekorator akzeptiert auch zwei Parameter, das Prototypobjekt und den Attributnamen. Aber es gibt keine Attribute, die Objekte beschreiben. Warum? Dies hat damit zu tun, wie TypeScript Eigenschaftsdekoratoren initialisiert. Derzeit gibt es keine Möglichkeit, beim Definieren eines Mitglieds eines Prototypobjekts ein Instanzattribut zu beschreiben. Funktion propertyDecorator(Wert: Zeichenfolge, Mädchen){ Rückgabefunktion (Prototyp, Schlüssel) { console.log('Prototypobjekt empfangen, dekorierten Eigenschaftsnamen, Eigenschaftsdeskriptor', Girl.prototype === Prototyp) } } Funktion thinFace(Prototyp, Schlüssel){ console.log(Prototyp, Schlüssel) } Klasse Mädchen { @thinFace @propertyDecorator('Immobiliendekorateur', Mädchen) öffentliches Alter: Zahl = 18; } sei g = neues Mädchen(); console.log(g.age); // 18 Andere Möglichkeiten zum Schreiben von DekoratorenDas Folgende ist eine Kombination mehrerer Dekoratoren. Zusätzlich zu den drei oben genannten gibt es auch Accessor-Dekoratoren und Parameter-Dekoratoren. Beim Zusammenstellen dieser Dekorateure ergibt sich eine Ausführungsreihenfolge. Funktion classDecorator(Wert: Zeichenfolge){ console.log(Wert) Rückgabefunktion(){} } Funktion propertyDecorator(Wert: Zeichenfolge) { console.log(Wert) Rückgabefunktion(){ console.log('Eigenschaftsdekorator') } } Funktion methodDecorator(Wert: Zeichenfolge) { console.log(Wert) Rückgabefunktion(){ console.log('Methodendekorator') } } Funktion paramDecorator(Wert: Zeichenfolge) { console.log(Wert) Rückgabefunktion(){ Konsole.log('paramDecorator') } } Funktion AccessDecorator(Wert: Zeichenfolge) { console.log(Wert) Rückgabefunktion(){ console.log('Zugriffsdekorator') } } Funktion thinFace(){ console.log('dünnes Gesicht') } Funktion IncreasingEyes() { console.log('Vergrößere deine Augen') } @thinFace @classDecorator('Klassendekorateur') Klasse Mädchen { @propertyDecorator('Eigenschaftsdekorateur') Alter: Zahl = 18; @AccessDecorator('Zugriffsdekorator') hol dir Stadt(){} @methodDecorator('Methodendekorator') @ErhöhteAugen Gesichtswert(){ console.log('ursprüngliches Gesicht') } getAge(@paramDecorator('Parameter-Dekorator') Name: Zeichenfolge){} } Nachdem Sie diesen kompilierten Code ausgeführt haben, werden Sie feststellen, dass die Reihenfolge dieser Zugriffsmethoden folgende ist: Eigenschaftsdekorator -> Zugriffsdekorator -> Methodendekorator -> Parameterdekorator -> Klassendekorator. Ausführlichere Informationen zur Verwendung finden Sie in der Dokumentation der offiziellen Website: https://www.tslang.cn/docs/handbook/decorators.html#decorator-factories Decorator-LaufzeitcodeanalyseDekoratoren werden in Browsern nicht unterstützt und können nicht direkt verwendet werden. Sie müssen mithilfe von Tools in ausführbaren Code für Browser kompiliert werden. Analysieren Sie den vom Tool kompilierten Code. Datei face.js generieren: tsc --target ES5 --experimentalDecorators face.ts Öffnen Sie die Datei face.js und Sie sehen einen komprimierten Code, den Sie formatieren können. Schauen wir uns zunächst diesen Code an: __schmücken([ propertyDecorator('Eigenschaftsdekorator') ], Girl.prototype, "Alter", void 0); __schmücken([ AccessDecorator('Zugriffsdecorator') ], Girl.prototype, "Stadt", null); __schmücken([ methodDecorator('Methodendekorator'), Zunehmende Augen ], Girl.prototype, "Gesichtswert", null); __schmücken([ __param(0, paramDecorator('Parameter-Dekorator')) ], Girl.prototype, "getAge", null); Mädchen = __decorate([ dünnesGesicht, classDecorator('Klassendekorateur') ], Mädchen); Die Funktion von __decorate besteht darin, die Dekoratorfunktion auszuführen. Aus diesem Code lassen sich viele Informationen entnehmen, die die oben gezogene Schlussfolgerung bestätigen. Aus der Aufrufreihenfolge von __decorate können wir erkennen, dass bei der gemeinsamen Verwendung mehrerer Dekoratortypen die Reihenfolge Attribut-Dekorator -> Accessor-Dekorator -> Methoden-Dekorator -> Parameter-Dekorator -> Klassen-Dekorator ist. Die Funktion __decorate wird aufgerufen und die übergebenen Parameter sind je nach verwendetem Dekoratortyp unterschiedlich. Der erste übergebene Parameter ist derselbe, nämlich ein Array, um sicherzustellen, dass er mit der Reihenfolge übereinstimmt, in der wir schreiben. Jedes Element ist die ausgewertete Dekorationsfunktion. Wenn @propertyDecorator() geschrieben wird, wird es sofort ausgeführt, um die Dekorationsfunktion abzurufen, was mit der obigen Analyse übereinstimmt. Klassendekoratoren verwenden die Klasse als zweites Argument, andere Dekoratoren verwenden das Prototypobjekt als zweites Argument, den Eigenschaftsnamen als drittes und das vierte ist null oder void 0. Der Wert von void 0 ist undefiniert, was bedeutet, dass keine Parameter übergeben werden. Merken Sie sich die Anzahl und den Wert der an die Funktion __decorate übergebenen Parameter. Wenn Sie tiefer in den Quellcode von __decorate einsteigen, werden diese Werte verwendet, um zu bestimmen, wie viele Parameter bei der Ausführung der Dekorationsfunktion übergeben werden sollen. OK, schauen wir uns die Implementierung der Funktion __decorate an: // Diese Funktion existiert bereits, verwenden Sie sie direkt, andernfalls definieren Sie sie selbst var __decorate = (this && this.__decorate) || // Erhalte vier Parameter: //decorators speichert das Array der Dekoratorfunktionen und das Zielprototypobjekt|Klasse, //Schlüsselattributname, Beschreibung (nicht definiert oder null) Funktion (Dekoratoren, Ziel, Schlüssel, Beschreibung) { var c = Argumente.Länge, // Anzahl der Parameter abrufen r = c < 3 // Wenn die Anzahl der Parameter kleiner als drei ist, handelt es sich um einen Klassendekorator. Kann die Klasse direkt abgerufen werden? Ziel : desc === null // Wenn der vierte Parameter null ist, muss das Objekt beschrieben werden; der Eigenschaftendekorator übergibt void 0 und es gibt kein Beschreibungsobjekt. ?desc = Object.getOwnPropertyDescriptor(Ziel, Schlüssel) : Beschreibung, D; // Wenn die Methode Reflect.decorate bereitgestellt wird, rufen Sie sie direkt auf. Andernfalls implementieren Sie sie selbst, wenn (typeof Reflect === "Objekt" und typeof Reflect.decorate === "Funktion") r = Reflect.decorate(Dekoratoren, Ziel, Schlüssel, Beschreibung); anders // Die Ausführungsreihenfolge der Dekoratorfunktionen ist entgegengesetzt zur Reihenfolge, in der sie geschrieben werden, von unten nach oben für (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) // Hole die Dekoratorfunktion r = (c < 3 // Wenn die Anzahl der Parameter kleiner als 3 ist, handelt es sich um einen Klassendekorator. Soll die Dekoratorfunktion ausgeführt und direkt an die Klasse übergeben werden? d(r) : c > 3 // Wenn die Anzahl der Parameter größer als drei ist und es sich um einen Methodendekorator, Accessor-Dekorator oder Parameterdekorator handelt, das übergebene Beschreibungsobjekt ausführen? d(Ziel, Schlüssel, r) : d(Ziel, Schlüssel) // Es handelt sich um einen Eigenschaftendekorator und das Beschreibungsobjekt wird nicht übergeben) || r; // Legen Sie das Beschreibungsobjekt für das dekorierte Attribut fest, hauptsächlich für Methoden und Attribute/*** * Für den Wert von r gibt es zwei Fälle. * Einer ist der Wert, der oben durch Object.getOwnPropertyDescriptor erhalten wurde. * Der andere ist der Rückgabewert nach Ausführung der Dekoratorfunktion, der als Beschreibungsobjekt verwendet wird. * Im Allgemeinen geben Dekorationsfunktionen keine Werte zurück. */ return c > 3 && r && Object.defineProperty(Ziel, Schlüssel, r),r; }; Der obige Parameterdekorator ruft eine Funktion namens __params auf. var __param = (dieses && dies.__param) || Funktion (ParamIndex, Dekorator) { Rückgabefunktion (Ziel, Schlüssel) { Dekorator (Ziel, Schlüssel, Parameterindex); } }; Der Zweck besteht darin, die Parameterposition paramIndex an die Dekoratorfunktion zu übergeben. Ich glaube, dass Sie nach dem Lesen des kompilierten Quellcodes ein tieferes Verständnis von Dekoratoren haben werden. Oben finden Sie Einzelheiten zum JS-Dekorationsmuster und zum TypeScript-Dekorator. Weitere Informationen zu JS finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM! Das könnte Sie auch interessieren:
|
<<: MySQL-Techniken zum schnellen Datenvergleich
Best Practices für die Web-Frontend-Optimierung: ...
Code kopieren Der Code lautet wie folgt: <!DOC...
Inhaltsverzeichnis Überblick Beispiel 1) Objekt e...
Szenario: Die Daten in einer Tabelle müssen mithi...
In diesem Artikel wird der spezifische Code von R...
Inhaltsverzeichnis Nginx fungiert als Proxy für z...
1. CSS3-Animation ☺CSS3-Animationen sind viel ein...
Inhaltsverzeichnis Vorwort Architektur auf einen ...
Nach der Eingabe von yum in Linux erscheint die E...
In diesem Artikel werden mehrere wichtige Zero-Co...
Inhaltsverzeichnis Containerhierarchie Der Prozes...
Einführung Da die Anforderungen an die Computerte...
Sie können eine Funktion schreiben: Verwenden Sie...
Dieser Artikel veranschaulicht anhand von Beispie...
<br />Vorheriger Artikel: Webdesign-Tutorial...