Au fil des ans, JavaScript est devenu un langage de programmation puissant et adaptable, évoluant constamment pour répondre aux besoins changeants des développeurs.
L'une de ses avancées relativement récentes est l'objet Proxy, qui permet aux programmeurs de créer des objets puissants et flexibles capables d'intercepter et de modifier les opérations clés sur d'autres objets.
Cet article se penche sur les capacités de Proxy en JavaScript , couvrant sa syntaxe, ses attributs, ses applications typiques, ses points forts, ses limites, ses instances illustratives et ses approches recommandées.
Un proxy est un objet qui enveloppe un autre objet et intercepte les opérations de base sur celui-ci, telles que l'accès, l'attribution et la suppression de propriétés. Le proxy est un aspect crucial de JavaScript qui permet aux développeurs d'écrire du code plus polyvalent et plus robuste.
Le but de cet article est de fournir une compréhension complète de Proxy en JavaScript, englobant sa syntaxe, ses caractéristiques, ses avantages, ses inconvénients, ses illustrations et les techniques recommandées.
JavaScript's Proxy est une capacité qui permet la création d'objets capables de modifier et de personnaliser les opérations de base effectuées sur d'autres objets.
Pour établir un objet Proxy, deux composants sont nécessaires : un objet cible et un objet gestionnaire. L'objet cible est celui sur lequel les opérations doivent être interceptées, tandis que l'objet gestionnaire est chargé de contenir les pièges, ou méthodes, utilisés pour intercepter ces opérations.
Voici un exemple montrant comment créer un objet Proxy de base :
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
Dans cet exemple, nous générons un objet cible qui a deux caractéristiques : nom et âge. Nous générons également un objet gestionnaire doté d'un piège get pour capturer tous les efforts de lecture d'une propriété sur l'objet cible. Après cela, nous produisons un objet Proxy en fournissant les objets cible et gestionnaire au constructeur Proxy. Enfin, nous récupérons la propriété name de l'objet Proxy, qui appelle le piège get et envoie un message à la console.
Les pièges sont des méthodes qui interceptent les opérations sur l'objet cible. Il existe plusieurs pièges que vous pouvez utiliser avec un objet Proxy, notamment get, set, has, deleteProperty, etc.
Voici un bref aperçu de certains des pièges les plus couramment utilisés :
get : Ce trap intercepte les tentatives de lecture d'une propriété sur l'objet cible. Il prend deux arguments : l'objet cible et la propriété en cours d'accès. L'interruption renvoie la valeur de la propriété.
set : ce déroutement capture tout effort pour établir une propriété sur l'objet cible. Il nécessite trois paramètres : l'objet cible lui-même, la propriété en cours d'établissement et la valeur mise à jour de cette propriété. Le mécanisme a la capacité de modifier la valeur en cours d'établissement, ou il peut générer une erreur pour interdire l'établissement de la valeur.
has : ce trap intercepte les tentatives de vérification de l'existence d'une propriété sur l'objet cible. Il prend deux arguments : l'objet cible et la propriété vérifiée. L'interruption renvoie une valeur booléenne indiquant si la propriété existe ou non.
deleteProperty : ce piège intercepte les tentatives de suppression d'une propriété de l'objet cible. Il prend deux arguments : l'objet cible et la propriété en cours de suppression. L'interruption peut supprimer la propriété ou générer une erreur pour empêcher la suppression de la propriété.
Les objets proxy possèdent une caractéristique fascinante qui permet de les invalider, ce qui fait que leurs pièges n'interceptent plus les opérations sur l'objet cible. Pour construire un objet Proxy qui peut être invalidé, utilisez la fonction Proxy.revocable()
.
Voici un exemple:
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
Dans cet exemple, nous créons un objet Proxy révocable à l'aide de la méthode Proxy.revocable()
. Nous accédons ensuite à la propriété name de l'objet Proxy, qui déclenche le piège get et enregistre un message dans la console. Nous révoquons ensuite l'objet Proxy à l'aide de la méthode revoke()
, ce qui signifie que toute autre tentative d'accès aux propriétés de l'objet Proxy échouera.
Une autre caractéristique intéressante des objets Proxy est qu'ils peuvent être utilisés pour implémenter des modèles d'héritage en JavaScript. En utilisant un objet Proxy comme prototype d'un autre objet, vous pouvez intercepter les recherches de propriétés et personnaliser le comportement de la chaîne de prototypes.
Voici un exemple :
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
Dans cet exemple, un objet parent est défini et possède un attribut de nom. Ensuite, nous créons un objet gestionnaire avec un piège get qui empêche toute demande de lecture pour les propriétés de l'objet enfant. L'interruption utilise la méthode Reflect.get() pour revenir à l'objet parent si la propriété est absente de l'objet enfant.
Ensuite, en utilisant un objet Proxy comme prototype et l'objet gestionnaire comme gestionnaire, nous construisons un objet enfant. Enfin, nous accédons à la propriété name de l'objet enfant et la modifions, ce qui déclenche les interruptions get et set et enregistre les messages dans la console.
Un cas d'utilisation de Proxy consiste à mettre en cache des appels de fonction coûteux. Dans cet exemple, nous créons un objet Proxy qui met en cache le résultat d'un appel de fonction en fonction de ses arguments.
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
Dans cet exemple, nous définissons une fonction appelée calculateCost
qui prend un prix et un taux de taxe et renvoie le coût avec la taxe. Nous créons ensuite un objet cache en utilisant la classe Map
.
Ensuite, nous créons un objet Proxy appelé proxy
qui intercepte les appels de fonction à l'aide de l'interruption apply
. L'interruption apply
est appelée chaque fois que la fonction est appelée et elle reçoit les arguments de la fonction sous forme de tableau. Nous utilisons les arguments pour générer une clé de cache et vérifier si le résultat est déjà dans le cache. Si c'est le cas, nous renvoyons le résultat mis en cache. Sinon, nous calculons le résultat et le stockons dans le cache.
Enfin, nous invoquons la fonction proxy
en utilisant différents arguments et observons que le résultat est stocké dans le cache pour les appels ultérieurs avec des arguments identiques.
Un autre cas d'utilisation de Proxy consiste à valider les propriétés d'un objet. Dans cet exemple, nous créons un objet Proxy qui valide la longueur d'une propriété de chaîne.
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
Dans cet exemple, nous définissons un objet appelé user
avec un name
et une propriété password
. Nous créons ensuite un objet Proxy appelé proxy
qui intercepte les affectations de propriété à l'aide de l'interruption set
. L'interruption set
est appelée chaque fois qu'une propriété est affectée, et elle reçoit le nom de la propriété, la nouvelle valeur et l'objet cible.
Nous utilisons le piège set
pour vérifier si la propriété assignée est la propriété password
et si la valeur contient moins de 8 caractères. Si c'est le cas, nous lançons une erreur. Sinon, nous définissons la valeur de la propriété sur l'objet cible.
Nous utilisons l'objet proxy
pour attribuer diverses valeurs à la propriété password
et notons que toute valeur de moins de 8 caractères déclenche une erreur.
Un autre cas d'utilisation courant de Proxy consiste à consigner les accès et les affectations de propriété d'objet. Dans cet exemple, nous créons un objet Proxy qui enregistre les accès aux propriétés et les affectations.
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]
Dans cet exemple, nous définissons un objet appelé user
avec un name
et une propriété email
. Nous créons ensuite un objet proxy appelé proxy
qui intercepte les accès aux propriétés et les affectations à l'aide des pièges get
et set
.
L'interruption get
est appelée à chaque accès à une propriété et reçoit le nom de la propriété et l'objet cible. Dans cet exemple, nous enregistrons un message dans la console indiquant que la propriété est en cours d'accès, puis nous renvoyons la valeur de la propriété à partir de l'objet cible.
L'interruption set
est appelée chaque fois qu'une propriété est affectée, et elle reçoit le nom de la propriété, la nouvelle valeur et l'objet cible. Dans cet exemple, nous enregistrons un message dans la console indiquant que la propriété est affectée, puis nous définissons la valeur de la propriété sur l'objet cible.
Enfin, nous accédons et attribuons diverses propriétés à l'aide de l'objet proxy
et observons que les messages sont enregistrés dans la console.
Comportement personnalisable : avec les objets proxy, vous pouvez intercepter et personnaliser les opérations de base sur d'autres objets, ce qui vous permet de créer des fonctionnalités avancées telles que le contrôle d'accès, la mise en cache et la journalisation.
Héritage : les objets proxy offrent la possibilité d'implémenter des modèles d'héritage en JavaScript, ce qui peut conduire à un code plus polyvalent et évolutif.
Révocable : les objets proxy peuvent être désactivés ou révoqués après leur création, ce qui les rend utiles pour limiter la portée de l'objet proxy ou pour des raisons de sécurité.
Malgré le fait que le proxy est avec nous depuis longtemps, toutes les versions de navigateurs ne peuvent pas prendre en charge cette fonctionnalité.
De plus, l'utilisation de proxys peut affecter négativement les performances de votre application, surtout si vous les utilisez trop souvent.
Il est important de comprendre la signification de l'utilisation d'un proxy. Il ne faut pas lui faire confiance pour les moments critiques pour l'application, tels que la validation importante de l'entrée de l'utilisateur.
Soyez conscient des restrictions : Avant d'implémenter un proxy dans votre code, soyez conscient des restrictions qu'il impose et comment elles peuvent affecter la vitesse et la sécurité de votre application.
Les objets proxy ne doivent être utilisés qu'en cas d'absolue nécessité, car ils peuvent affecter les performances de votre code.
Testez soigneusement : lorsque vous utilisez des objets Proxy, assurez-vous de tester soigneusement et soyez attentif à tout comportement potentiellement inattendu.
Respectez les normes : pour rendre votre code simple à lire et à maintenir, respectez les conventions acceptées et les meilleures pratiques lors de la mise en œuvre des objets Proxy.
L'article se penche sur les fonctionnalités avancées de Proxy, telles que les modèles d'héritage et la possibilité de créer des objets Proxy révocables.
Quel que soit votre niveau d'expérience en tant que développeur, comprendre Proxy en JavaScript est fondamental pour élever votre code à un niveau supérieur.
En raison de son adaptabilité et de sa puissance, Proxy constitue un instrument essentiel pour tout développeur JavaScript aspirant à construire facilement des applications complexes.