Im Laufe der Jahre hat sich JavaScript zu einer leistungsstarken und anpassungsfähigen Programmiersprache entwickelt, die sich ständig weiterentwickelt, um den sich ändernden Anforderungen von Entwicklern gerecht zu werden.
Eine seiner relativ neuen Weiterentwicklungen ist das Proxy-Objekt, das es Programmierern ermöglicht, leistungsstarke und flexible Objekte zu erstellen, die in der Lage sind, Schlüsseloperationen an anderen Objekten abzufangen und zu ändern.
Dieser Artikel befasst sich mit den Funktionen von Proxy in JavaScript und behandelt dessen Syntax, Attribute, typische Anwendungen, Stärken, Einschränkungen, veranschaulichende Beispiele und empfohlene Ansätze.
Ein Proxy ist ein Objekt, das ein anderes Objekt umhüllt und grundlegende Operationen daran abfängt, wie z. B. den Zugriff auf, die Zuweisung und das Löschen von Eigenschaften. Proxy ist ein entscheidender Aspekt von JavaScript, der es Entwicklern ermöglicht, vielseitigeren und robusteren Code zu schreiben.
Ziel dieses Artikels ist es, ein umfassendes Verständnis von Proxy in JavaScript zu vermitteln, einschließlich seiner Syntax, Eigenschaften, Vorteile, Nachteile, Illustrationen und empfohlenen Techniken.
Der Proxy von JavaScript ist eine Funktion, die die Erstellung von Objekten ermöglicht, mit denen die grundlegenden Operationen, die an anderen Objekten ausgeführt werden, geändert und angepasst werden können.
Um ein Proxy-Objekt einzurichten, sind zwei Komponenten erforderlich: ein Zielobjekt und ein Handler-Objekt. Das Zielobjekt ist das Objekt, auf dem Operationen abgefangen werden sollen, während das Handler-Objekt für die Speicherung der Traps oder Methoden verantwortlich ist, die zum Abfangen dieser Operationen verwendet werden.
Hier ist ein Beispiel, das zeigt, wie ein einfaches Proxy-Objekt erstellt wird:
const target = { name: 'John', age: 25, }; const handler = { get: function(target, prop) { console.log(`Getting property ${prop}`); return target[prop]; }, }; const proxy = new Proxy(target, handler); console.log(proxy.name); // Getting property name // John
In diesem Beispiel generieren wir ein Zielobjekt, das zwei Merkmale aufweist: Name und Alter. Wir generieren außerdem ein Handlerobjekt mit einer Get-Trap, um alle Versuche zum Lesen einer Eigenschaft des Zielobjekts zu erfassen. Danach erzeugen wir ein Proxy-Objekt, indem wir dem Proxy-Konstruktor die Ziel- und Handlerobjekte bereitstellen. Zuletzt rufen wir die Namenseigenschaft des Proxy-Objekts ab, das den Get-Trap aufruft und eine Nachricht an die Konsole ausgibt.
Traps sind Methoden, die Operationen am Zielobjekt abfangen. Es gibt mehrere Traps, die Sie mit einem Proxy-Objekt verwenden können, darunter get, set, has, deleteProperty und mehr.
Hier ist ein kurzer Überblick über einige der am häufigsten verwendeten Fallen:
get : Diese Falle fängt Versuche ab, eine Eigenschaft des Zielobjekts zu lesen. Es benötigt zwei Argumente: das Zielobjekt und die Eigenschaft, auf die zugegriffen wird. Die Falle gibt den Wert der Eigenschaft zurück.
set : Diese Falle erfasst jeden Versuch, eine Eigenschaft für das Zielobjekt einzurichten. Es sind drei Parameter erforderlich: das Zielobjekt selbst, die einzurichtende Eigenschaft und der aktualisierte Wert dieser Eigenschaft. Der Mechanismus kann den ermittelten Wert ändern oder einen Fehler erzeugen, der die Festlegung des Werts verhindert.
has : Diese Falle fängt Versuche ab, zu überprüfen, ob eine Eigenschaft für das Zielobjekt vorhanden ist. Es benötigt zwei Argumente: das Zielobjekt und die überprüfte Eigenschaft. Der Trap gibt einen booleschen Wert zurück, der angibt, ob die Eigenschaft vorhanden ist oder nicht.
deleteProperty : Diese Falle fängt Versuche ab, eine Eigenschaft aus dem Zielobjekt zu löschen. Es benötigt zwei Argumente: das Zielobjekt und die zu löschende Eigenschaft. Die Falle kann die Eigenschaft löschen oder einen Fehler auslösen, um zu verhindern, dass die Eigenschaft gelöscht wird.
Proxy-Objekte besitzen eine faszinierende Eigenschaft, die es ermöglicht, sie ungültig zu machen, was dazu führt, dass ihre Traps keine Operationen mehr am Zielobjekt abfangen. Um ein Proxy-Objekt zu erstellen, das ungültig gemacht werden kann, verwenden Sie die Funktion Proxy.revocable()
.
Hier ist ein Beispiel:
const target = { name: 'John', age: 25, }; const handler = { get: function(target, prop) { console.log(`Getting property ${prop}`); return target[prop]; }, }; const {proxy, revoke} = Proxy.revocable(target, handler); console.log(proxy.name); // Getting property name // John revoke(); console.log(proxy.name); // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked
In diesem Beispiel erstellen wir ein widerrufbares Proxy-Objekt mit der Methode Proxy.revocable()
. Anschließend greifen wir auf die Namenseigenschaft des Proxy-Objekts zu, das den Get-Trap auslöst und eine Nachricht an die Konsole protokolliert. Anschließend widerrufen wir das Proxy-Objekt mit der revoke()
Methode, was bedeutet, dass alle weiteren Versuche, auf Eigenschaften des Proxy-Objekts zuzugreifen, fehlschlagen.
Ein weiteres interessantes Merkmal von Proxy-Objekten besteht darin, dass sie zur Implementierung von Vererbungsmustern in JavaScript verwendet werden können. Durch die Verwendung eines Proxy-Objekts als Prototyp eines anderen Objekts können Sie Eigenschaftssuchen abfangen und das Verhalten der Prototypenkette anpassen.
Hier ist ein Beispiel:
const parent = { name: 'John', }; const handler = { get: function(target, prop) { console.log(`Getting property ${prop}`); if (!(prop in target)) { return Reflect.get(parent, prop); } return target[prop]; }, }; const child = new Proxy({}, handler); console.log(child.name); // Getting property name // John child.name = 'Bob'; console.log(child.name); // Getting property name // Bob console.log(parent.name); // John
In diesem Beispiel ist ein übergeordnetes Objekt definiert und verfügt über ein Namensattribut. Anschließend erstellen wir ein Handlerobjekt mit einer Get-Trap, die jegliche Leseanforderungen für die Eigenschaften des untergeordneten Objekts verhindert. Die Falle verwendet die Reflect.get()-Methode, um auf das übergeordnete Objekt zurückzugreifen, wenn die Eigenschaft im untergeordneten Objekt fehlt.
Anschließend erstellen wir ein untergeordnetes Objekt, indem wir ein Proxy-Objekt als Prototyp und das Handler-Objekt als Handler verwenden. Schließlich erhalten wir Zugriff auf die Namenseigenschaft des untergeordneten Objekts und ändern diese, wodurch Get- und Set-Traps ausgelöst und Meldungen an der Konsole protokolliert werden.
Ein Anwendungsfall für Proxy ist das Zwischenspeichern teurer Funktionsaufrufe. In diesem Beispiel erstellen wir ein Proxy-Objekt, das das Ergebnis eines Funktionsaufrufs basierend auf seinen Argumenten zwischenspeichert.
function calculateCost(price, taxRate) { console.log('Calculating cost...'); return price * (1 + taxRate); } const cache = new Map(); const proxy = new Proxy(calculateCost, { apply(target, thisArg, args) { const key = args.join('-'); if (cache.has(key)) { console.log('Returning cached result...'); return cache.get(key); } else { const result = Reflect.apply(target, thisArg, args); cache.set(key, result); return result; } }, }); console.log(proxy(10, 0.2)); // Calculating cost... 12 console.log(proxy(10, 0.2)); // Returning cached result... 12 console.log(proxy(20, 0.2)); // Calculating cost... 24 console.log(proxy(20, 0.3)); // Calculating cost... 26 console.log(proxy(20, 0.3)); // Returning cached result... 26
In diesem Beispiel definieren wir eine Funktion namens calculateCost
, die einen Preis und einen Steuersatz verwendet und die Kosten inklusive Steuer zurückgibt. Anschließend erstellen wir mithilfe der Map
Klasse ein Cache-Objekt.
Als Nächstes erstellen wir ein Proxy-Objekt namens proxy
, das Funktionsaufrufe mithilfe der apply
Trap abfängt. Der apply
Trap wird bei jedem Aufruf der Funktion aufgerufen und empfängt die Funktionsargumente als Array. Wir verwenden die Argumente, um einen Cache-Schlüssel zu generieren und prüfen, ob das Ergebnis bereits im Cache liegt. Wenn ja, geben wir das zwischengespeicherte Ergebnis zurück. Andernfalls berechnen wir das Ergebnis und speichern es im Cache.
Schließlich rufen wir die proxy
Funktion mit unterschiedlichen Argumenten auf und beobachten, dass das Ergebnis für nachfolgende Aufrufe mit identischen Argumenten im Cache gespeichert wird.
Ein weiterer Anwendungsfall für Proxy ist die Validierung von Objekteigenschaften. In diesem Beispiel erstellen wir ein Proxy-Objekt, das die Länge einer Zeichenfolgeneigenschaft validiert.
const user = { name: 'John', password: 'secret', }; const proxy = new Proxy(user, { set(target, prop, value) { if (prop === 'password' && value.length < 8) { throw new Error('Password must be at least 8 characters long'); } target[prop] = value; return true; }, }); console.log(proxy.name); // John console.log(proxy.password); // secret proxy.password = '12345678'; console.log(proxy.password); // 12345678 proxy.password = '123'; // Error
In diesem Beispiel definieren wir ein Objekt namens „ user
mit einer name
und einer password
. Anschließend erstellen wir ein Proxy-Objekt namens proxy
, das Eigenschaftszuweisungen mithilfe des set
Trap abfängt. Der set
Trap wird bei jeder Zuweisung einer Eigenschaft aufgerufen und erhält den Eigenschaftsnamen, den neuen Wert und das Zielobjekt.
Wir verwenden den set
Trap, um zu prüfen, ob es sich bei der zugewiesenen Eigenschaft um die password
handelt und ob der Wert weniger als 8 Zeichen lang ist. Wenn dies der Fall ist, geben wir einen Fehler aus. Andernfalls legen wir den Eigenschaftswert für das Zielobjekt fest.
Wir verwenden das proxy
Objekt, um der password
verschiedene Werte zuzuweisen und beachten, dass alle Werte mit einer Länge von weniger als 8 Zeichen einen Fehler auslösen.
Ein weiterer häufiger Anwendungsfall für Proxy ist die Protokollierung von Objekteigenschaftenzugriffen und -zuweisungen. In diesem Beispiel erstellen wir ein Proxy-Objekt, das Eigenschaftszugriffe und -zuweisungen protokolliert.
const user = { name: 'John', email: '[email protected]', }; const proxy = new Proxy(user, { get(target, prop) { console.log(`Getting ${prop} property`); return target[prop]; }, set(target, prop, value) { console.log(`Setting ${prop} property to ${value}`); target[prop] = value; return true; }, }); console.log(proxy.name); // Getting name property -> John proxy.email = '[email protected]'; // Setting email property to [email protected] console.log(proxy.email); // Getting email property -> [email protected]
In diesem Beispiel definieren wir ein Objekt namens „ user
mit einem name
und einer email
Eigenschaft. Anschließend erstellen wir ein Proxy-Objekt namens proxy
, das Eigenschaftszugriffe und Zuweisungen mithilfe der get
und set
-Traps abfängt.
Der get
Trap wird bei jedem Zugriff auf eine Eigenschaft aufgerufen und empfängt den Eigenschaftsnamen und das Zielobjekt. In diesem Beispiel protokollieren wir eine Nachricht an die Konsole, die angibt, dass auf die Eigenschaft zugegriffen wird, und geben dann den Eigenschaftswert vom Zielobjekt zurück.
Der set
Trap wird bei jeder Zuweisung einer Eigenschaft aufgerufen und erhält den Eigenschaftsnamen, den neuen Wert und das Zielobjekt. In diesem Beispiel protokollieren wir eine Nachricht an die Konsole, die angibt, dass die Eigenschaft zugewiesen wird, und legen dann den Eigenschaftswert für das Zielobjekt fest.
Schließlich greifen wir mithilfe des proxy
Objekts auf verschiedene Eigenschaften zu, weisen diese zu und beobachten, dass Nachrichten in der Konsole protokolliert werden.
Anpassbares Verhalten : Mit Proxy-Objekten können Sie grundlegende Vorgänge an anderen Objekten abfangen und anpassen und so erweiterte Funktionen wie Zugriffskontrolle, Caching und Protokollierung erstellen.
Vererbung : Proxy-Objekte bieten die Möglichkeit, Vererbungsmuster in JavaScript zu implementieren, was zu vielseitigerem und skalierbarerem Code führen kann.
Widerrufbar : Proxy-Objekte können nach ihrer Erstellung deaktiviert oder widerrufen werden, was sie zur Einschränkung des Umfangs des Proxy-Objekts oder aus Sicherheitsgründen nützlich macht.
Obwohl der Proxy schon lange bei uns ist, können nicht alle Browserversionen diese Funktionalität unterstützen.
Darüber hinaus kann sich die Verwendung von Proxys negativ auf die Leistung Ihrer Anwendung auswirken, insbesondere wenn Sie sie zu oft verwenden.
Es ist wichtig, die Bedeutung der Verwendung eines Proxys zu verstehen. In anwendungskritischen Momenten, wie z. B. der wichtigen Validierung von Benutzereingaben, sollte man ihm nicht vertrauen.
Beachten Sie die Einschränkungen : Bevor Sie einen Proxy in Ihren Code implementieren, sollten Sie sich über die damit verbundenen Einschränkungen im Klaren sein und wissen, wie diese sich auf die Geschwindigkeit und Sicherheit Ihrer Anwendung auswirken können.
Proxy-Objekte sollten nur verwendet werden, wenn dies unbedingt erforderlich ist, da sie die Leistung Ihres Codes beeinträchtigen können.
Sorgfältig testen : Achten Sie bei der Verwendung von Proxy-Objekten darauf, sorgfältig zu testen und auf potenziell unerwartetes Verhalten zu achten.
Halten Sie sich an Normen : Um die Lesbarkeit und Wartung Ihres Codes zu vereinfachen, halten Sie sich bei der Implementierung von Proxy-Objekten an anerkannte Konventionen und Best Practices.
Der Artikel befasst sich mit erweiterten Funktionen von Proxy, wie z. B. Vererbungsmustern und der Möglichkeit, widerrufliche Proxy-Objekte zu erstellen.
Unabhängig von Ihrem Erfahrungsniveau als Entwickler ist das Verständnis von Proxy in JavaScript von grundlegender Bedeutung, um Ihren Code auf ein höheres Niveau zu heben.
Aufgrund seiner Anpassungsfähigkeit und Leistungsfähigkeit stellt Proxy ein wichtiges Instrument für jeden JavaScript-Entwickler dar, der komplexe Anwendungen problemlos erstellen möchte.