L’enveloppement des fonctions JavaScript vous permet d’ajouter une logique commune aux fonctions que vous ne contrôlez pas, comme les fonctions natives et externes. De nombreuses bibliothèques JavaScript, comme les agents TrackJS, doivent envelopper des fonctions externes pour effectuer leur travail. L’ajout de wrappers nous permet d’écouter la télémétrie, les erreurs et les journaux dans votre code, sans que vous ayez besoin d’appeler notre API explicitement.
Vous pouvez vouloir envelopper une fonction pour ajouter une logique d’instrumentation ou de débogage temporaire. Vous pouvez également changer le comportement d’une bibliothèque externe sans avoir besoin de modifier la source.
Enveloppements de fonctions de base
Parce que JavaScript est merveilleusement dynamique, nous pouvons envelopper une fonction en redéfinissant simplement la fonction avec quelque chose de nouveau. Par exemple, considérez cet enveloppement de myFunction
:
Dans cet exemple trivial, nous avons enveloppé le myFunction
original et ajouté un message de journalisation. Mais il y a beaucoup de choses que nous n’avons pas traitées :
- Comment faire passer les arguments de la fonction ?
- Comment maintenir la portée (la valeur de
this
) ? - Comment obtenir la valeur de retour ?
- Que faire si une erreur se produit ?
Pour gérer ces choses, nous devons devenir un peu plus astucieux dans notre enveloppement.
Notez que nous ne faisons pas qu’invoquer la fonction dans cet exemple, mais que nous la call
-ons avec la valeur de this
et les arguments a
, b
, et c
. La valeur de this
sera transmise à partir de l’endroit où vous attachez votre fonction enveloppée, Window
dans cet exemple.
Nous avons également entouré l’ensemble de la fonction dans un bloc try/catch
afin que nous puissions invoquer une logique personnalisée en cas d’erreur, la relancer, ou retourner une valeur par défaut.
Enveloppements de fonctions avancés
L’exemple d’enveloppement de base fonctionnera 90% du temps, mais si vous construisez des bibliothèques partagées, comme les agents TrackJS, ce n’est pas suffisant ! Pour envelopper nos fonctions comme un pro, il y a quelques cas limites que nous devons traiter :
- Qu’en est-il des arguments non déclarés ou inconnus ?
- Comment correspondre à la signature de la fonction ?
- Comment refléter les propriétés de la fonction ?
Il y a 3 changements subtils mais importants. Premièrement (#1), nous avons nommé la fonction. Cela semble redondant, mais le code utilisateur peut vérifier la valeur de function.name
, il est donc important de maintenir le nom lors du wrapping.
Le deuxième changement (#2) est dans la façon dont nous avons appelé la fonction wrapped, en utilisant apply
au lieu de call
. Cela nous permet de faire passer un objet arguments
, qui est un objet de type tableau de tous les arguments passés à la fonction au moment de l’exécution. Cela nous permet de prendre en charge les fonctions qui peuvent avoir un nombre indéfini ou variable d’arguments.
Avec apply
, nous n’avons pas besoin des arguments a
, b
, et c
définis dans la signature de la fonction. Mais en continuant à déclarer les mêmes arguments que la fonction originale, nous maintenons l’arité de la fonction. C’est-à-dire que Function.length
renvoie le nombre d’arguments définis dans la signature, et cela reflétera la fonction originale.
La dernière modification (#3) copie toutes les propriétés spécifiées par l’utilisateur de la fonction originale sur notre enveloppement.
Limitations
Cet enveloppement est complet, mais il y a toujours des limites en JavaScript. Plus précisément, il est difficile d’envelopper correctement une fonction avec un prototype non standard, comme un constructeur d’objet. C’est un cas d’utilisation mieux résolu par l’héritage.
En général, changer le prototype d’une fonction est possible, mais ce n’est pas une bonne idée. Il y a de sérieuses implications de performance et des effets secondaires involontaires dans la manipulation des prototypes.
Respecter l’environnement
L’enveloppement de fonction vous donne beaucoup de pouvoir pour instrumenter et manipuler l’environnement JavaScript. Vous avez la responsabilité de manier ce pouvoir à bon escient. Si vous construisez des wrappers de fonctions, veillez à respecter l’utilisateur et l’environnement dans lequel vous opérez. Il se peut que d’autres wrappers soient en place, que d’autres écouteurs soient attachés aux événements et qu’il y ait des attentes concernant les API de fonctions. Allez-y doucement et ne cassez pas le code externe.