Metaprogrammierung in JavaScript und TypeScript

Prolog


Ich möchte Ihrem Gericht eine Reihe von Mini-Statuen vorstellen, die die Techniken und Grundlagen der Metaprogrammierung beschreiben. Ich werde hauptsächlich über die Verwendung bestimmter Techniken in JavaScript oder in TypeScript schreiben
Dies ist der erste (und hoffentlich nicht der letzte) Artikel in der Reihe.


Was ist Metaprogrammierung?


Metaprogrammierung ist eine Programmiertechnik, bei der Computerprogramme andere Programme als ihre Daten behandeln können. Dies bedeutet, dass ein Programm so gestaltet werden kann, dass es andere Programme liest, generiert, analysiert oder transformiert und sich sogar während der Ausführung selbst ändert. In einigen Fällen können Programmierer so die Anzahl der Codezeilen minimieren, um eine Lösung auszudrücken, was wiederum die Entwicklungszeit verkürzt .

Eine ziemlich verwirrende Beschreibung, aber der Hauptvorteil der Metaprogrammierung ist verständlich:


... können Programmierer so die Anzahl der Codezeilen minimieren, um die Lösung zu implementieren, was wiederum die Entwicklungszeit verkürzt


In der Tat hat Metaprogrammierung viel Gesicht und Gestalt. Und Sie können lange darüber diskutieren, „wo die Metaprogrammierung endet und die Programmierung selbst beginnt“.


Für mich selbst habe ich folgende Regeln akzeptiert:


  1. Die Metaprogrammierung befasst sich nicht mit Geschäftslogik, ändert sie nicht und beeinflusst sie in keiner Weise.
  2. Wenn Sie den gesamten Metaprogrammiercode entfernen, sollte dies das Programm nicht (radikal) beeinflussen.

In JavaScript ist die Metaprogrammierung ein relativ neuer Trend, dessen Basisbaustein der Deskriptor ist.


JavaScript-Deskriptor


Deskriptor ist eine Art Beschreibung (Metainformation) einer bestimmten Eigenschaft oder Methode in einem Objekt.


Das Verstehen und ordnungsgemäße Bearbeiten dieses Objekts ( Deskriptors ) ermöglicht viel mehr als nur das Erstellen und Ändern von Methoden oder Eigenschaften in Objekten.
Auch der Deskriptor hilft beim Verständnis der Arbeit mit Dekorateuren (mehr dazu im nächsten Artikel).


Stellen Sie sich zur Klarheit vor, unser Objekt ist eine Beschreibung der Wohnung.
Wir beschreiben das Objekt unserer Wohnung:


let apt = { floor: 12, number: '12B', size: 3400, bedRooms: 3.4, bathRooms: 2, price: 400000, amenities: {...} }; 

Lassen Sie uns feststellen, welche der Eigenschaften geändert werden können und welche nicht.


Zum Beispiel kann der Boden oder die Gesamtgröße der Wohnung nicht geändert werden, aber die Anzahl der Zimmer oder Badezimmer ist durchaus möglich.
Wir haben also folgende Anforderung: Machen Sie es in geeigneten Objekten unmöglich, die Eigenschaften zu ändern: Boden und Größe .


Um dieses Problem zu lösen, benötigen wir nur Deskriptoren für jede dieser Eigenschaften. Um den Deskriptor abzurufen , verwenden wir die statische Methode getOwnPropertyDescriptor , die zur Klasse Object gehört .


 let descriptor = Object.getOwnPropertyDescriptor(todoObject, 'floor'); console.log(descriptor); // Output { value: 12, writable: true, enumerable: true, configurable: true } 

Lassen Sie uns der Reihe nach analysieren:
value: any - tatsächlich derselbe Wert, der irgendwann der Etage zugewiesen wurde
beschreibbar: boolean - legt fest, ob der Wert geändert werden soll oder nicht
enumerable: boolean - bestimmt, ob die Etageeigenschaft aufgelistet werden kann oder nicht - (dazu später mehr).
konfigurierbar: boolean - Definiert die Möglichkeit, Änderungen am Deskriptorobjekt vorzunehmen.


Um zu verhindern, dass die Bodeneigenschaft nach der Initialisierung geändert werden kann, muss der Wert von writable in false geändert werden.
Um die Eigenschaften eines Deskriptors zu ändern , gibt es eine statische Methode defineProperty , die das Objekt selbst, den Namen der Eigenschaft und den Deskriptor verwendet .


 Object.defineProperty(apt, 'floor', {writable: false}); 

In diesem Beispiel übergeben wir nicht das gesamte Deskriptorobjekt , sondern nur eine beschreibbare Eigenschaft mit dem Wert false .
Versuchen wir nun, den Wert in der Bodeneigenschaft zu ändern:


 apt.floor = 44; console.log(apt.floor); // output 12 

Der Wert hat sich nicht geändert, und bei Verwendung von 'use strict' wird eine Fehlermeldung angezeigt:


Kann nicht der schreibgeschützten Eigenschaft 'Etage' des Objekts zuweisen ...

Und jetzt können wir den Wert nicht mehr ändern. Wir können jedoch weiterhin beschreibbar -> true zurückgeben und dann die Bodeneigenschaft ändern. Um dies zu vermeiden, muss der Wert der konfigurierbaren Eigenschaft im Deskriptor auf false geändert werden.


 Object.defineProperty(apt, 'floor', {writable: false, configurable: false}); 

Wenn wir jetzt versuchen, den Wert einer der Eigenschaften unseres Deskriptors zu ändern ...


 Object.defineProperty(apt, 'floor', {writable: true, configurable: true}); 

Als Antwort erhalten wir:


TypeError: Eigenschaft: Etage kann nicht neu definiert werden
Mit anderen Worten, wir können weder den Wert des Bodens noch seinen Deskriptor mehr ändern.

Fassen Sie zusammen


Um den Eigenschaftswert im Objekt unverändert zu lassen, muss die Konfiguration dieser Eigenschaft registriert werden: {beschreibbar: falsch, konfigurierbar: falsch} .


Dies kann wie bei der Eigenschaftsinitialisierung erfolgen:


 Object.defineProperty(apt, 'floor', {value: 12, writable: false, configurable: false}); 

Oder danach.


 Object.defineProperty(apt, 'floor', {writable: false, configurable: false}); 

Betrachten Sie am Ende ein Beispiel mit einer Klasse:


 class Apartment { constructor(apt) { this.apt = apt; } getFloor() { return this.apt.floor } } let apt = { floor: 12, number: '12B', size: 3400, bedRooms: 3.4, bathRooms: 2, price: 400000, amenities: {...} }; 

Ändern Sie die getFloor-Methode:


 Apartment.prototype.getFloor = () => { return 44 }; let myApt = new Apartment(apt); console.log(myApt); // output will be changed. 44 

Ändern Sie nun den Deskriptor der Methode getFloor () :


 Object.defineProperty(Apartment.prototype, 'getFloor', {writable: false, configurable: false}); Apartment.prototype.getFloor = () => { return 44 }; let myApt = new Apartment(apt); console.log(myApt); // output will be original. 12 

Ich hoffe, dieser Artikel gibt etwas mehr Aufschluss darüber, was Deskriptor ist und wie er verwendet werden kann.


Alles, was oben geschrieben wurde, behauptet nicht, absolut wahr oder das einzig Richtige zu sein.

Source: https://habr.com/ru/post/de450540/


All Articles