Wrapping JavaScript functions lets you add common logic to functions you do not control, like native and external functions. Viele JavaScript-Bibliotheken, wie die TrackJS-Agenten, müssen externe Funktionen umhüllen, um ihre Arbeit zu erledigen. Durch das Hinzufügen von Wrappern können wir in Ihrem Code auf Telemetrie, Fehler und Protokolle achten, ohne dass Sie unsere API explizit aufrufen müssen.
Sie können eine Funktion umhüllen, um Instrumentierung oder temporäre Debugging-Logik hinzuzufügen. Sie können auch das Verhalten einer externen Bibliothek ändern, ohne den Quellcode zu modifizieren.
Basic Function Wrapping
Da JavaScript wunderbar dynamisch ist, können wir eine Funktion umhüllen, indem wir die Funktion einfach mit etwas Neuem umdefinieren. Betrachten Sie zum Beispiel dieses Wrapping von myFunction
:
In diesem trivialen Beispiel haben wir das ursprüngliche myFunction
gewrappt und eine Logging-Meldung hinzugefügt. Aber es gibt eine Menge Dinge, die wir nicht behandelt haben:
- Wie übergeben wir Funktionsargumente?
- Wie erhalten wir den Gültigkeitsbereich (den Wert von
this
)? - Wie erhalten wir den Rückgabewert?
- Was, wenn ein Fehler auftritt?
Um mit diesen Dingen umzugehen, müssen wir unser Wrapping etwas cleverer gestalten.
Beachte, dass wir die Funktion in diesem Beispiel nicht einfach nur aufrufen, sondern call
-sie mit dem Wert von this
und den Argumenten a
, b
und c
verbinden. Der Wert von this
wird von der Stelle weitergegeben, an der Sie Ihre gewrappte Funktion anhängen, Window
in diesem Beispiel.
Wir haben die gesamte Funktion auch in einen try/catch
-Block eingeschlossen, so dass wir im Falle eines Fehlers eine benutzerdefinierte Logik aufrufen, sie erneut auslösen oder einen Standardwert zurückgeben können.
Erweitertes Function Wrapping
Das einfache Wrapping-Beispiel wird in 90% der Fälle funktionieren, aber wenn man gemeinsam genutzte Bibliotheken wie die TrackJS-Agenten erstellt, ist das nicht gut genug! Um unsere Funktionen wie ein Profi zu wrappen, gibt es einige Randfälle, mit denen wir umgehen sollten:
- Was ist mit nicht deklarierten oder unbekannten Argumenten?
- Wie passen wir die Funktionssignatur an?
- Wie spiegeln wir die Funktionseigenschaften?
Es gibt 3 subtile, aber wichtige Änderungen. Erstens (#1) haben wir die Funktion benannt. Es scheint redundant zu sein, aber der Benutzercode kann den Wert von function.name
überprüfen, daher ist es wichtig, den Namen beim Wrapping beizubehalten.
Die zweite Änderung (#2) besteht darin, wie wir die gewrappte Funktion aufrufen, indem wir apply
statt call
verwenden. Dies ermöglicht es uns, ein arguments
-Objekt zu übergeben, das ein Array-ähnliches Objekt aller Argumente ist, die der Funktion zur Laufzeit übergeben werden. Dies ermöglicht es uns, Funktionen zu unterstützen, die eine undefinierte oder variable Anzahl von Argumenten haben können.
Mit apply
brauchen wir die in der Funktionssignatur definierten Argumente a
, b
und c
nicht. Indem wir jedoch weiterhin die gleichen Argumente wie die ursprüngliche Funktion deklarieren, behalten wir die Arität der Funktion bei. Das heißt, Function.length
gibt die Anzahl der in der Signatur definierten Argumente zurück, und dies spiegelt die ursprüngliche Funktion wider.
Die letzte Änderung (#3) kopiert alle benutzerspezifischen Eigenschaften der ursprünglichen Funktion in unsere Umhüllung.
Einschränkungen
Diese Umhüllung ist gründlich, aber es gibt immer Einschränkungen in JavaScript. Insbesondere ist es schwierig, eine Funktion mit einem Nicht-Standard-Prototyp, wie z.B. einem Objektkonstruktor, korrekt zu wrappen. Dies ist ein Anwendungsfall, der besser durch Vererbung gelöst wird.
Im Allgemeinen ist es möglich, den Prototyp einer Funktion zu ändern, aber es ist keine gute Idee. Es gibt schwerwiegende Auswirkungen auf die Leistung und unbeabsichtigte Nebeneffekte bei der Manipulation von Prototypen.
Respect the Environment
Function wrapping gibt Ihnen eine Menge Macht, die JavaScript-Umgebung zu instrumentieren und zu manipulieren. Sie haben die Verantwortung, diese Macht weise zu nutzen. Wenn Sie Funktions-Wrapper bauen, achten Sie darauf, dass Sie den Benutzer und die Umgebung, in der Sie arbeiten, respektieren. Möglicherweise gibt es andere Wrapper, andere Listener für Ereignisse und Erwartungen an Funktions-APIs. Gehen Sie behutsam vor und unterbrechen Sie keinen externen Code.