JavaScript Proxy und Reflect
JavaScript Proxy und Reflect: Operationen mit Traps abfangen, Validierung, Zugriffskontrolle und Logging einfach umsetzen.
JavaScript-Proxys ermöglichen es Ihnen, grundlegende Operationen auf Objekten abzufangen und neu zu definieren — das Lesen einer Eigenschaft, das Schreiben einer Eigenschaft, das Prüfen, ob ein Schlüssel existiert, das Aufrufen einer Funktion und mehr. In Kombination mit der Reflect-API, die dieselben Operationen auf dem „Standard"-Weg ausführt, bieten Proxys einen sauberen, offiziellen Mechanismus, um Verhalten zu Objekten hinzuzufügen, ohne diese direkt zu verändern.
Dieses Kapitel behandelt, was ein Proxy ist, die häufigsten Traps (get, set, apply, has, deleteProperty), wie Reflect Proxys ergänzt und einige praxisnahe Muster wie Validierung, Zugriffskontrolle und Logging.
Was ist ein Proxy
Ein Proxy umschließt ein Zielobjekt und einen Handler. Der Handler ist eine Sammlung von Funktionen, die als Traps bezeichnet werden — jede fängt eine bestimmte Operation ab. Wenn Sie mit dem Proxy interagieren, wird anstelle des Standardverhaltens der passende Trap ausgeführt. Ist für eine Operation kein Trap definiert, leitet der Proxy sie unverändert an das Zielobjekt weiter.
Das ist nützlich, wenn Sie querschnittliches Verhalten — Logging, Validierung, Standardwerte, Zugriffsregeln — auf ein Objekt anwenden möchten, ohne dessen eigenen Code zu berühren.
Proxy-Syntax
const proxy = new Proxy(target, handler);target: Das ursprüngliche Objekt, dessen Operationen Sie abfangen möchten.handler: Ein object, dessen Methoden (Traps) das Verhalten der Operationen festlegen.
Anschließend verwenden Sie proxy genau wie das ursprüngliche Objekt; der Unterschied besteht darin, dass Ihre Traps dazwischengeschaltet sind.
Den get-Trap verstehen
Der get-Trap fängt Eigenschaftszugriffe auf das Zielobjekt ab. Er erhält das target, den property-Schlüssel und den receiver (den Proxy selbst). Er wird häufig zum Protokollieren von Zugriffen, zur dynamischen Berechnung von Eigenschaften oder zur Rückgabe von Standardwerten für fehlende Schlüssel eingesetzt.
Beispiel:
Dieser Code richtet einen Proxy ein, um Eigenschaftszugriffe auf einem object zu protokollieren.
- Handler: Definiert einen
get-Trap, der die aufgerufene Eigenschaft protokolliert. - Zielobjekt: Enthält die Eigenschaften
nameundage. - Proxy: Umschließt das
target-object mit demhandler.
Wenn auf proxy.name zugegriffen wird, gibt es „Getting name" aus und gibt „John" zurück. Das ist nützlich für die Überwachung oder das Debugging von Eigenschaftszugriffen.
Objektoperationen mit set- und apply-Traps manipulieren
Der set-Trap
Der set-Trap kann Regeln für Eigenschaftszuweisungen durchsetzen und sicherstellen, dass Eigenschaften bestimmte Typen haben oder bestimmte Bedingungen erfüllen.
Beispiel:
Dieser Code richtet einen Proxy ein, um Eigenschaftszuweisungen auf einem object zu validieren und zu protokollieren.
- Handler: Definiert einen
set-Trap, der die Eigenschaftageauf gültige Werte prüft und Zuweisungsversuche protokolliert. - Proxy: Umschließt das
target-object mit demhandler.
Wenn proxy.age gesetzt wird, prüft der Trap, ob es sich um ein gültiges Alter (0–150) handelt. Bei einem ungültigen Wert wird ein Fehler protokolliert und eine Ausnahme ausgelöst.
Der apply-Trap
Die apply-Methode in einem JavaScript-Proxy fängt Funktionsaufrufe ab. Sie nimmt drei Argumente entgegen:
- target: Die ursprüngliche aufgerufene Funktion.
- thisArg: Der Wert von
thisinnerhalb der Funktion. - argumentsList: Ein array der an die Funktion übergebenen Argumente.
Beispiel:
Dieser Code richtet einen Proxy ein, um Funktionsaufrufe und ihre Argumente zu protokollieren.
- Handler: Definiert einen
apply-Trap, der die Argumente beim Funktionsaufruf protokolliert. - Funktion:
sumaddiert zwei Zahlen. - Proxy: Umschließt die
sum-Funktion mit demhandler.
Im gezeigten Code protokolliert der apply-Trap die Argumente und ruft dann die ursprüngliche Funktion mit target.apply(thisArg, argumentsList) auf. Das ist nützlich für Logging, Debugging oder die dynamische Veränderung von Funktionsverhalten.
Der has-Trap
Der has-Trap fängt den in-Operator ab. Ein häufiger Einsatz ist das Verbergen von „privaten" Schlüsseln (per Konvention Namen, die mit _ beginnen), sodass sie von außen scheinbar nicht existieren.
Beispiel:
Obwohl _secret im Zielobjekt weiterhin existiert, gibt der in-Operator false zurück, sodass der Schlüssel für Code, der das object abfragt, effektiv verborgen ist.
Der deleteProperty-Trap
Der deleteProperty-Trap fängt den delete-Operator ab und ermöglicht es, bestimmte Schlüssel vor dem Löschen zu schützen. Er muss true zurückgeben, wenn das Löschen erlaubt ist, oder im Strict-Modus eine Ausnahme auslösen, um es zu blockieren.
Beispiel:
JavaScript-Proxys sind leistungsstark, sollten aber mit Bedacht eingesetzt werden. Übermäßiger Einsatz von Proxys kann den Code schwerer verständlich und wartbar machen. Beachten Sie, dass Proxys im Vergleich zu nativen Objekten einen leichten Leistungsoverhead verursachen.
Reflect-API
Reflect ist ein eingebautes object, das für jede abfangbare Operation eine Methode bereitstellt — genau die gleiche Menge, die von Proxy-Traps abgedeckt wird (Reflect.get, Reflect.set, Reflect.has, Reflect.deleteProperty, Reflect.apply usw.). Jede Methode führt die Standard-Version dieser Operation aus.
Damit ist Reflect der natürliche Begleiter von Proxy: Innerhalb eines Traps möchte man in der Regel ein bestimmtes Verhalten hinzufügen und dann die Operation normal ablaufen lassen. Der Aufruf der passenden Reflect-Methode tut genau das und leitet den receiver korrekt weiter (wichtig für Getter/Setter), was ein einfaches target[property] nicht tut. Dieses Muster wird in den praktischen Beispielen unten verwendet.
Reflect-Methoden geben außerdem Werte zurück, anstatt Ausnahmen auszulösen — zum Beispiel gibt Reflect.set einen boolean zurück, der den Erfolg anzeigt —, was Operationen vorhersehbarer macht als ihre Operator- oder Object.*-Entsprechungen.
Hier ist ein kurzer Überblick über die wichtigsten Reflect-Methoden:
1. Reflect.get()
Diese Methode wird verwendet, um den Wert einer Eigenschaft aus einem object abzurufen.
Beispiel:
2. Reflect.set()
Diese Methode wird verwendet, um den Wert einer Eigenschaft in einem object zu setzen.
Beispiel:
3. Reflect.has()
Diese Methode prüft, ob eine Eigenschaft in einem object vorhanden ist.
Beispiel:
4. Reflect.deleteProperty()
Diese Methode löscht eine Eigenschaft aus einem object.
Beispiel:
5. Reflect.ownKeys()
Diese Methode gibt alle eigenen Eigenschaftsschlüssel eines object zurück.
Beispiel:
6. Reflect.apply()
Diese Methode ruft eine Zielfunktion mit den angegebenen Argumenten auf.
Beispiel:
7. Reflect.construct()
Diese Methode wird verwendet, um eine neue Instanz eines object zu erstellen.
Beispiel:
Diese Beispiele zeigen, wie Sie Reflect-Methoden verwenden können, um allgemeine Operationen auf Objekten auf eine sauberere und einheitlichere Weise durchzuführen.
Praktische Anwendungsfälle von JavaScript-Proxys
Beispiel 1: Automatische Eigenschaftsinitialisierung
Beschreibung: Verwenden Sie JavaScript-Proxys, um undefinierte Eigenschaften in einem object automatisch zu initialisieren. Das kann in Situationen nützlich sein, in denen Objekte dynamisch mit Daten befüllt werden, wie z. B. Benutzereinstellungen oder Konfigurationen, die anfangs möglicherweise nicht gesetzt sind.
Dieser Code erstellt einen Proxy, der prüft, ob eine Eigenschaft auf einem object existiert. Wenn nicht, setzt der Proxy automatisch einen Standardwert dafür. Das hilft dabei, Fehler durch fehlende Eigenschaften zu vermeiden.
Beispiel 2: Zugriffskontrolle
Beschreibung: Proxys können Lese- oder Schreibberechtigungen für Objekteigenschaften durchsetzen. Dieses Beispiel zeigt einen Proxy, der bestimmte Eigenschaften basierend auf vordefinierten Regeln vom Lesen oder Schreiben ausschließt — besonders nützlich für die Verwaltung des Zugriffs auf sensible Daten.
Dieser Code sichert ein object, indem er den Zugriff auf seine Eigenschaften kontrolliert. Es blockiert das Lesen von sensitiveData und verhindert das Ändern von readOnly-Eigenschaften, um die Datensicherheit zu gewährleisten.
Beispiel 3: Logging und Debugging
Beschreibung: Proxys können verwendet werden, um Interaktionen mit einem object zu protokollieren, was beim Debugging und der Überwachung von Operationen hilft. Dieses Beispiel erstellt einen Proxy, der alle Gets, Sets und Methodenaufrufe auf einem object protokolliert.
Dieser Code verfolgt jedes Mal, wenn jemand auf eine Eigenschaft des object zugreift oder sie ändert — ideal, um zu verstehen, was der Code wann macht.
Beispiel 4: Datenvalidierung
Beschreibung: Verwenden Sie Proxys für die On-the-fly-Validierung von Objekteigenschaften. Das ist besonders nützlich, um die Datenintegrität sicherzustellen, wenn Objekte in einer Anwendung dynamisch aktualisiert werden.
Dieses Beispiel zeigt, wie man mit JavaScript's Proxy Eigenschaftsänderungen validiert und protokolliert. Das validator-object prüft, ob die Eigenschaft age eine gültige Zahl zwischen 0 und 150 ist. Wenn nicht, wird ein Fehler protokolliert und eine Ausnahme ausgelöst. Andernfalls wird der neue Wert protokolliert und die Eigenschaft aktualisiert. Das person-object verwendet diesen Validator, um seine Eigenschaft age zu verwalten und sicherzustellen, dass ungültige Altersangaben abgefangen und protokolliert werden.
Fazit
Wenn Sie JavaScript-Proxys beherrschen, können Sie steuern und erweitern, wie Objekte sich verhalten, ohne sie direkt zu verändern. Proxys können Validierungs- und Zugriffsregeln durchsetzen, Standardwerte bereitstellen und Logging- oder Debugging-Werkzeuge antreiben, während Reflect die zugrunde liegenden Operationen sauber und vorhersehbar hält. Sparsam eingesetzt helfen sie dabei, dynamischere und sicherere Anwendungen zu erstellen.
Für einen tieferen Einblick in die Operationen, die Proxys abfangen, lesen Sie Property Getter und Setter und Property Flags und Descriptors. Für das oben gezeigte Validierungsmuster sind Fehlerbehandlung mit try...catch und Klassen nützliche Ergänzungen.