Cette page a été traduite à partir de l'anglais par la communauté.Vous pouvez contribuer en rejoignant la communauté francophone sur MDN Web Docs.
Proxy
Baseline Widely available
Cette fonctionnalité est bien établie et fonctionne sur de nombreux appareils et versions de navigateurs. Elle est disponible sur tous les navigateurs depuis septembre 2016.
Un objetProxy permet de créer un intermédiaire pour un autre objet et qui peut intercepter et redéfinir certaines opérations fondamentales pour lui.
Dans cet article
Description
Un objetProxy permet de créer un objet qui peut être utilisé à la place de l'objet original en redéfinissant certaines opérations fondamentales comme l'accès, la modification et la définition de propriétés. Les objetsProxy sont généralement utilisés pour journaliser l'accès aux propriétés, valider, formater ou nettoyer des valeurs saisies, etc.
La création d'un objetProxy se fait avec deux paramètres :
cibleL'objet original devant lequel on veut placer un intermédiaire
gestionnaireUn objet qui définit les opérations qui seront interceptées et comment celles-ci seront redéfinies.
Dans l'exemple qui suit, on a une cible simple avec deux propriétés et un gestionnaire encore plus simple, sans propriété.
const cible = { message1: "coucou", message2: "tout le monde",};const gestionnaire1 = {};const proxy1 = new Proxy(cible, gestionnaire1);Le gestionnaire étant vide, le proxy se comporte à l'identique de la cible :
console.log(proxy1.message1); // coucouconsole.log(proxy1.message2); // tout le mondePour adapter le proxy, on définit des fonctions sur le gestionnaire :
const cible = { message1: "coucou", message2: "tout le monde",};const gestionnaire2 = { get(cible, prop, recepteur) { return "le monde"; },};const proxy2 = new Proxy(cible, gestionnaire2);Ici, on a fourni une implémentation du gestionnaireget(), qui intercepte les tentatives d'accès aux propriétés de la cible.
Les fonctions d'un gestionnaire sont parfois appelées destrappes, car les appels originaux tombent dans ces trappes. Celle qui est utilisée dansgestionnaire2 redéfinit l'accès pour toutes les propriétés :
console.log(proxy2.message1); // le mondeconsole.log(proxy2.message2); // le mondeAvecReflect, on peut rediriger certains accesseurs vers leur comportement original et en redéfinir d'autres :
const cible = { message1: "coucou", message2: "tout le monde",};const gestionnaire3 = { get(cible, prop, recepteur) { if (prop === "message2") { return "le monde"; } return Reflect.get(...arguments); },};const proxy3 = new Proxy(cible, gestionnaire3);console.log(proxy3.message1); // coucouconsole.log(proxy3.message2); // le mondeConstructeur
Proxy()Crée un nouvel objet
Proxy.
Méthodes statiques
Proxy.revocable()Crée un objet
Proxyrévocable.
Exemples
>Exemple simple
Dans ce court exemple, on renvoie le nombre37 comme valeur par défaut lorsque la propriété nommée n'est pas présente dans l'objet. Pour cela, on utilise le gestionnaire correspondant àget().
const handler = { get(obj, prop) { return prop in obj ? obj[prop] : 37; },};const p = new Proxy({}, handler);p.a = 1;p.b = undefined;console.log(p.a, p.b);// 1, undefinedconsole.log("c" in p, p.c);// false, 37Proxy « invisible »
Dans cet exemple, le proxy transfère toutes les opérations qui sont appliquées à l'objet cible.
const target = {};const p = new Proxy(target, {});p.a = 37;// L'opération est transmise à la cible par le proxyconsole.log(target.a);// 37// L'opération a bien été transmiseOn notera que bien que ceci fonctionne pour les objets JavaScript construits dans les scripts, ça ne fonctionne pas pour les objets natifs de l'environnement (comme les éléments du DOM dans un navigateur).
Validation
En utilisant unProxy, on peut simplement valider les valeurs passées à un objet. Dans cet exemple, on utilise le gestionnaire correspondant àset().
let validateur = { set: function (obj, prop, valeur) { if (prop === "age") { if (!Number.isInteger(valeur)) { throw new TypeError("Cet a n'est pas un entier."); } if (valeur > 200) { throw new RangeError("Cet âge semble invalide."); } } // Le comportement par défaut : enregistrer la valeur obj[prop] = valeur; // On indique le succès de l'opération return true; },};const personne = new Proxy({}, validateur);personne.age = 100;console.log(personne.age); // 100personne.age = "jeune"; // lève une exceptionpersonne.age = 300; // lève une exceptionÉtendre un constructeur
En utilisant une fonction proxy, on peut étendre un constructeur avec un nouveau constructeur. Dans cet exemple, on utilise les gestionnaires correspondants àconstruct() etapply().
function etendre(sup, base) { var descripteur = Object.getOwnPropertyDescriptor( base.prototype, "constructor", ); base.prototype = Object.create(sup.prototype); var gestionnaire = { construct: function (cible, args) { var obj = Object.create(base.prototype); this.apply(cible, obj, args); return obj; }, apply: function (cible, that, args) { sup.apply(that, args); base.apply(that, args); }, }; var proxy = new Proxy(base, gestionnaire); descripteur.value = proxy; Object.defineProperty(base.prototype, "constructor", descripteur); return proxy;}var Personne = function (nom) { this.nom = nom;};var Garcon = etendre(Personne, function (nom, âge) { this.âge = âge;});Garcon.prototype.genre = "M";var Pierre = new Garcon("Pierre", 13);console.log(Pierre.genre); // "M"console.log(Pierre.nom); // "Pierre"console.log(Pierre.âge); // 13Manipuler les nœuds DOM
Dans cet exemple, on utiliseProxy afin qu'un attribut alterne entre deux éléments différents : si on définit l'attribut sur un élément, il sera retiré de l'autre.
On crée un objetvue qui est un proxy pour l'objet avec uneselected. Le gestionnaire du proxy définit la fonctionset().
Lorsqu'on affecte un élément HTML àvue.selected, l'attribut'aria-selected' de l'élément est placé àtrue. Si on affecte ensuite un autre élément àvue.selected, ce nouvel élément aura l'attribut'aria-selected' défini àtrue et l'élément précédent verra son attribut'aria-selected' automatiquement défini àfalse.
let vue = new Proxy( { selected: null, }, { set(obj, prop, nouvelleValeur) { let ancienneValeur = obj[prop]; if (prop === "selected") { if (ancienneValeur) { ancienneValeur.setAttribute("aria-selected", "false"); } if (nouvelleValeur) { nouvelleValeur.setAttribute("aria-selected", "true"); } } // Le comportement par défaut : enregistrer la valeur obj[prop] = nouvelleValeur; // On indique le succès de l'opération return true; }, },);const element1 = document.getElementById("elem-1");const element2 = document.getElementById("elem-2");// on sélectionne element1vue.selected = element1;console.log(`element1 : ${element1.getAttribute("aria-selected")}`);// element1 : true// on sélectionne element2 et cela entraîne// la déselection automatique de element1vue.selected = element2;console.log(`element1 : ${element1.getAttribute("aria-selected")}`);// element1 : falseconsole.log(`element2 : ${element2.getAttribute("aria-selected")}`);// element2 : trueCorriger une valeur et ajouter une propriété supplémentaire
Dans l'exemple qui suit, le proxyproduits évalue la valeur passée et la convertit en tableau si besoin. L'objet prend également en charge la propriété supplémentairedernierNavigateur à la fois comme accesseur et mutateur.
let produits = new Proxy( { navigateurs: ["Internet Explorer", "Netscape"], }, { get(obj, prop) { // Une propriété supplémentaire if (prop === "dernierNavigateur") { return obj.navigateurs[obj.navigateurs.length - 1]; } // Le comportement par défaut : renvoyer la valeur return obj[prop]; }, set(obj, prop, valeur) { // Une propriété supplémentaire if (prop === "dernierNavigateur") { obj.navigateurs.push(valeur); return true; } // on convertit la valeur si ce n'est pas un tableau if (typeof valeur === "string") { valeur = [valeur]; } // Le comportement par défaut : enregistrer la valeur obj[prop] = valeur; // On indique le succès de l'opération return true; }, },);console.log(produits.navigateurs);// ['Internet Explorer', 'Netscape']produits.navigateurs = "Firefox";// on passe une chaîneconsole.log(produits.navigateurs);// ['Firefox'] <- pas de problème, elle est convertie en tableauproduits.dernierNavigateur = "Chrome";console.log(produits.navigateurs);// ['Firefox', 'Chrome']console.log(produits.dernierNavigateur);// 'Chrome'Trouver un élément dans un tableau grâce à sa propriété
Dans cet exemple, ce proxy étend le tableau avec des fonctionnalités supplémentaires. Ici, on définit des propriétés sans utiliserObject.defineProperties(). Cet exemple pourrait être adapté pour trouver la ligne d'un tableau à partir d'une de ces cellules (la cible serait alorstable.rows).
let produits = new Proxy( [ { nom: "Firefox", type: "navigateur" }, { nom: "SeaMonkey", type: "navigateur" }, { nom: "Thunderbird", type: "client mail" }, ], { get(obj, prop) { // Le comportement par défaut : on renvoie la valeur // prop est généralement un entier if (prop in obj) { return obj[prop]; } // On obtient le nombre de produits // un alias pour products.length if (prop === "nombre") { return obj.length; } let resultat, types = {}; for (let produit of obj) { if (produit.nom === prop) { resultat = produit; } if (types[produit.type]) { types[produit.type].push(produit); } else { types[produit.type] = [produit]; } } // Obtenir un produit grâce à un nom if (resultat) { return resultat; } // Obtenir un produit par type if (prop in types) { return types[prop]; } // Obtenir les types de produits if (prop === "types") { return Object.keys(types); } return undefined; }, },);console.log(produits[0]);// { nom: 'Firefox', type: 'navigateur' }console.log(produits["Firefox"]);// { nom: 'Firefox', type: 'navigateur' }console.log(produits["Chrome"]);// undefinedconsole.log(produits.navigateur);// [{ nom: 'Firefox', type: 'navigateur' }, { nom: 'SeaMonkey', type: 'navigateur' }]console.log(produits.types);// ['navigateur', 'client mail']console.log(produits.nombre);// 3Un exemple avec toutes les trappes
Pour illustrer l'ensemble des trappes, on tente de « proxifier » un objet non natif : l'objet globaldocCookies créé grâce àcet exemple.
/* var docCookies = ... définir l'objet "docCookies" grâce à https://reference.codeproject.com/dom/document/cookie/simple_document.cookie_framework*/var docCookies = new Proxy(docCookies, { get(oTarget, sKey) { return oTarget[sKey] || oTarget.getItem(sKey) || undefined; }, set: function (oTarget, sKey, vValue) { if (sKey in oTarget) { return false; } return oTarget.setItem(sKey, vValue); }, deleteProperty: function (oTarget, sKey) { if ((!sKey) in oTarget) { return false; } return oTarget.removeItem(sKey); }, ownKeys: function (oTarget, sKey) { return oTarget.keys(); }, has: function (oTarget, sKey) { return sKey in oTarget || oTarget.hasItem(sKey); }, defineProperty: function (oTarget, sKey, oDesc) { if (oDesc && "value" in oDesc) { oTarget.setItem(sKey, oDesc.value); } return oTarget; }, getOwnPropertyDescriptor: function (oTarget, sKey) { var vValue = oTarget.getItem(sKey); return vValue ? { value: vValue, writable: true, enumerable: true, configurable: false, } : undefined; },});/* Test */console.log((docCookies.monCookie1 = "Première valeur"));console.log(docCookies.getItem("monCookie1"));docCookies.setItem("monCookie1", "Valeur modifiée");console.log(docCookies.monCookie1);Spécifications
| Specification |
|---|
| ECMAScript® 2026 Language Specification> # sec-proxy-objects> |