| [ Web Proxy ] |
| Viewing: https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Proxy | [Back] [Original] |
Get to know MDN better
Cette page a t traduite partir de l'anglais par la communaut. Vous pouvez contribuer en rejoignant la communaut francophone sur MDN Web Docs.
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 objet Proxy permet de crer un intermdiaire pour un autre objet et qui peut intercepter et redfinir certaines oprations fondamentales pour lui.
Un objet Proxy permet de crer un objet qui peut tre utilis la place de l'objet original en redfinissant certaines oprations fondamentales comme l'accs, la modification et la dfinition de proprits. Les objets Proxy sont gnralement utiliss pour journaliser l'accs aux proprits, valider, formater ou nettoyer des valeurs saisies, etc.
La cration d'un objet Proxy se fait avec deux paramtres :
cibleL'objet original devant lequel on veut placer un intermdiaire
gestionnaireUn objet qui dfinit les oprations qui seront interceptes et comment celles-ci seront redfinies.
Dans l'exemple qui suit, on a une cible simple avec deux proprits et un gestionnaire encore plus simple, sans proprit.
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); // coucou
console.log(proxy1.message2); // tout le monde
Pour adapter le proxy, on dfinit 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 implmentation du gestionnaire get(), qui intercepte les tentatives d'accs aux proprits de la cible.
Les fonctions d'un gestionnaire sont parfois appeles des trappes, car les appels originaux tombent dans ces trappes. Celle qui est utilise dans gestionnaire2 redfinit l'accs pour toutes les proprits :
console.log(proxy2.message1); // le monde
console.log(proxy2.message2); // le monde
Avec Reflect, on peut rediriger certains accesseurs vers leur comportement original et en redfinir 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); // coucou
console.log(proxy3.message2); // le monde
Proxy()Cre un nouvel objet Proxy.
Proxy.revocable()Cre un objet Proxy rvocable.
Dans ce court exemple, on renvoie le nombre 37 comme valeur par dfaut lorsque la proprit nomme n'est pas prsente 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, undefined
console.log("c" in p, p.c);
// false, 37
Dans cet exemple, le proxy transfre toutes les oprations qui sont appliques l'objet cible.
const target = {};
const p = new Proxy(target, {});
p.a = 37;
// L'opration est transmise la cible par le proxy
console.log(target.a);
// 37
// L'opration a bien t transmise
On 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 lments du DOM dans un navigateur).
En utilisant un Proxy, on peut simplement valider les valeurs passes 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 dfaut : enregistrer la valeur
obj[prop] = valeur;
// On indique le succs de l'opration
return true;
},
};
const personne = new Proxy({}, validateur);
personne.age = 100;
console.log(personne.age); // 100
personne.age = "jeune"; // lve une exception
personne.age = 300; // lve une exception
En utilisant une fonction proxy, on peut tendre un constructeur avec un nouveau constructeur. Dans cet exemple, on utilise les gestionnaires correspondants construct() et apply().
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); // 13
Dans cet exemple, on utilise Proxy afin qu'un attribut alterne entre deux lments diffrents : si on dfinit l'attribut sur un lment, il sera retir de l'autre.
On cre un objet vue qui est un proxy pour l'objet avec une selected. Le gestionnaire du proxy dfinit la fonction set().
Lorsqu'on affecte un lment HTML vue.selected, l'attribut 'aria-selected' de l'lment est plac true. Si on affecte ensuite un autre lment vue.selected, ce nouvel lment aura l'attribut 'aria-selected' dfini true et l'lment prcdent verra son attribut 'aria-selected' automatiquement dfini 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 dfaut : enregistrer la valeur
obj[prop] = nouvelleValeur;
// On indique le succs de l'opration
return true;
},
},
);
const element1 = document.getElementById("elem-1");
const element2 = document.getElementById("elem-2");
// on slectionne element1
vue.selected = element1;
console.log(`element1 : ${element1.getAttribute("aria-selected")}`);
// element1 : true
// on slectionne element2 et cela entrane
// la dselection automatique de element1
vue.selected = element2;
console.log(`element1 : ${element1.getAttribute("aria-selected")}`);
// element1 : false
console.log(`element2 : ${element2.getAttribute("aria-selected")}`);
// element2 : true
Dans l'exemple qui suit, le proxy produits value la valeur passe et la convertit en tableau si besoin. L'objet prend galement en charge la proprit supplmentaire dernierNavigateur la fois comme accesseur et mutateur.
let produits = new Proxy(
{
navigateurs: ["Internet Explorer", "Netscape"],
},
{
get(obj, prop) {
// Une proprit supplmentaire
if (prop === "dernierNavigateur") {
return obj.navigateurs[obj.navigateurs.length - 1];
}
// Le comportement par dfaut : renvoyer la valeur
return obj[prop];
},
set(obj, prop, valeur) {
// Une proprit supplmentaire
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 dfaut : enregistrer la valeur
obj[prop] = valeur;
// On indique le succs de l'opration
return true;
},
},
);
console.log(produits.navigateurs);
// ['Internet Explorer', 'Netscape']
produits.navigateurs = "Firefox";
// on passe une chane
console.log(produits.navigateurs);
// ['Firefox'] <- pas de problme, elle est convertie en tableau
produits.dernierNavigateur = "Chrome";
console.log(produits.navigateurs);
// ['Firefox', 'Chrome']
console.log(produits.dernierNavigateur);
// 'Chrome'
Dans cet exemple, ce proxy tend le tableau avec des fonctionnalits supplmentaires. Ici, on dfinit des proprits sans utiliser Object.defineProperties(). Cet exemple pourrait tre adapt pour trouver la ligne d'un tableau partir d'une de ces cellules (la cible serait alors table.rows).
let produits = new Proxy(
[
{ nom: "Firefox", type: "navigateur" },
{ nom: "SeaMonkey", type: "navigateur" },
{ nom: "Thunderbird", type: "client mail" },
],
{
get(obj, prop) {
// Le comportement par dfaut : on renvoie la valeur
// prop est gnralement 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 grce 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"]);
// undefined
console.log(produits.navigateur);
// [{ nom: 'Firefox', type: 'navigateur' }, { nom: 'SeaMonkey', type: 'navigateur' }]
console.log(produits.types);
// ['navigateur', 'client mail']
console.log(produits.nombre);
// 3
Pour illustrer l'ensemble des trappes, on tente de proxifier un objet non natif : l'objet global docCookies cr grce cet exemple.
/*
var docCookies = ... dfinir l'objet "docCookies" grce
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 = "Premire valeur"));
console.log(docCookies.getItem("monCookie1"));
docCookies.setItem("monCookie1", "Valeur modifie");
console.log(docCookies.monCookie1);
| Spcification |
|---|
| ECMAScript 2027 LanguageSpecification # sec-proxy-objects |
Cette page a t modifie le 17 dc. 2024 par les contributeurices du MDN.
ProxyCertaines parties de ce contenu sont protges par le droit d'auteur 19982026 des contributeurs individuels de mozilla.org. Contenu disponible sous une licence Creative Commons.
| Web Proxy Viewer | New URL | Original Page |