From 39f2114f9797eb51994966c6bb8ff1814c9a4da8 Mon Sep 17 00:00:00 2001 From: Florian Merz Date: Thu, 11 Feb 2021 12:36:08 +0100 Subject: unslug fr: move --- .../reference/global_objects/proxy/index.html | 407 +++++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 files/fr/web/javascript/reference/global_objects/proxy/index.html (limited to 'files/fr/web/javascript/reference/global_objects/proxy/index.html') diff --git a/files/fr/web/javascript/reference/global_objects/proxy/index.html b/files/fr/web/javascript/reference/global_objects/proxy/index.html new file mode 100644 index 0000000000..095515482d --- /dev/null +++ b/files/fr/web/javascript/reference/global_objects/proxy/index.html @@ -0,0 +1,407 @@ +--- +title: Proxy +slug: Web/JavaScript/Reference/Objets_globaux/Proxy +tags: + - ECMAScript 2015 + - JavaScript + - Proxy + - Reference +translation_of: Web/JavaScript/Reference/Global_Objects/Proxy +--- +
{{JSRef}}
+ +

L'objet Proxy est utilisé afin de définir un comportement sur mesure pour certaines opérations fondamentales (par exemple, l'accès aux propriétés, les affectations, les énumérations, les appels de fonctions, etc.).

+ +

Terminologie

+ +
+
gestionnaire (handler)
+
Un objet qui contient les trappes qui intercepteront les opérations.
+
trappes
+
Les méthodes qui fournissent l'accès aux propriétés. Ce concept est analogue aux trappes utilisées dans les systèmes d'exploitations.
+
cible
+
L'objet virtualisé par le proxy. Il est souvent utilisé comme objet de stockage. Les invariants (c'est-à-dire les éléments de sémantique qui restent inchangés) relatifs à la non-extensibilité et au caractère non-configurable des propriétés sont vérifiés par rapport à la cible.
+
+ +

Syntaxe

+ +
var p = new Proxy(cible, gestionnaire);
+
+ +

Paramètres

+ +
+
cible
+
Une cible (qui peut être n'importe quel objet, un tableau, une fonction, ou même un autre proxy) qu'on souhaite envelopper dans un Proxy.
+
gestionnaire
+
Un objet dont les propriétés sont des fonctions qui définissent le comportement du proxy lorsqu'on utilise une opération sur celui-ci.
+
+ +

Méthodes

+ +
+
{{jsxref("Proxy.revocable()")}}
+
Permet de créer un objet Proxy révocable.
+
+ +

Méthodes pour le gestionnaire

+ +

L'objet utilisé comme gestionnaire regroupe les différentes fonctions « trappes » pour le Proxy.

+ +
{{page('/fr/docs/Web/JavaScript/Reference/Objets_globaux/Proxy/handler', 'Méthodes') }}
+ +

Exemples

+ +

Exemple simple

+ +

Dans ce court exemple, on renvoie le nombre 37 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 à {{jsxref("Objets_globaux/Proxy/handler/get","get")}}.

+ +
var handler = {
+    get: function(obj, prop){
+        return prop in obj?
+            obj[prop] :
+            37;
+    }
+};
+
+var 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
+
+ +

Proxy « invisible »

+ +

Dans cet exemple, le proxy transfère toutes les opérations qui sont appliquées à l'objet cible.

+ +
var cible = {};
+var p = new Proxy(cible, {});
+
+p.a = 37; // L'opération est transmise à la cible par le proxy
+
+console.log(cible.a); // 37. L'opération a bien été transmise
+
+ +

Validation

+ +

En utilisant un Proxy, il devient simple de valider les valeurs passées à un objet. Dans cet exemple, on utilise le gestionnaire correspondant à {{jsxref("Objets_globaux/Proxy/handler/set","set")}}.

+ +
let validateur = {
+  set: function(obj, prop, valeur) {
+    if (prop === 'âge') {
+      if (!Number.isInteger(valeur)) {
+        throw new TypeError('Cet âge 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;
+  }
+};
+
+let personne = new Proxy({}, validateur);
+
+personne.âge = 100;
+console.log(personne.âge); // 100
+personne.âge = 'jeune';    // lève une exception
+personne.âge = 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 à {{jsxref("Objets_globaux/Proxy/handler/construct","construct")}} et {{jsxref("Objets_globaux/Proxy/handler/apply","apply")}}.

+ +
function étendre(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 Garçon = étendre(Personne, function(nom, âge) {
+  this.âge = âge;
+});
+
+Garçon.prototype.genre = "M";
+
+var Pierre = new Garçon("Pierre", 13);
+console.log(Pierre.genre);  // "M"
+console.log(Pierre.nom);  // "Pierre"
+console.log(Pierre.âge);  // 13
+ +

Manipuler les nœuds DOM

+ +

Parfois, on veut passer un attribut ou un nom de classe entre deux éléments différents. Dans cet exemple, on utilise le gestionnaire lié à {{jsxref("Objets_globaux/Proxy/handler/set","set")}}.

+ +
let vue = new Proxy({
+  selected: null
+},
+{
+  set: function(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;
+  }
+});
+
+let i1 = vue.selected = document.getElementById('item-1');
+console.log(i1.getAttribute('aria-selected')); // 'true'
+
+let i2 = vue.selected = document.getElementById('item-2');
+console.log(i1.getAttribute('aria-selected')); // 'false'
+console.log(i2.getAttribute('aria-selected')); // 'true'
+
+ +

Corriger une valeur et ajouter une propriété supplémentaire

+ +

Dans l'exemple qui suit, le proxy produits évalue la valeur passée et la convertit en tableau si besoin. L'objet supporte également la propriété supplémentaire dernierNavigateur à la fois comme accesseur et mutateur.

+ +
let produits = new Proxy({
+  navigateurs: ['Internet Explorer', 'Netscape']
+},
+{
+  get: function(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: function(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îne
+console.log(produits.navigateurs); // ['Firefox'] <- pas de problème, elle est convertie en tableau
+
+produits.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 utiliser {{jsxref("Objets_globaux/Object/defineProperties","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: function(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 résultat, types = {};
+
+    for (let produit of obj) {
+      if (produit.nom === prop) {
+        résultat = produit;
+      }
+      if (types[produit.type]) {
+        types[produit.type].push(produit);
+      } else {
+        types[produit.type] = [produit];
+      }
+    }
+
+    // Obtenir un produit grâce à un nom
+    if (résultat) {
+      return résultat;
+    }
+
+    // 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
+
+ +

Un exemple avec toutes les trappes

+ +

Pour illustrer l'ensemble des trappes, on tente de « proxifier » un objet non natif : l'objet global docCookies créé grâce à cet exemple.

+ +
/*
+  var docCookies = ... définir l'objet "docCookies" grâce à
+  https://developer.mozilla.org/en-US/docs/DOM/document.cookie#A_little_framework.3A_a_complete_cookies_reader.2Fwriter_with_full_unicode_support
+*/
+
+var docCookies = new Proxy(docCookies, {
+  "get": function (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);
+  },
+  "enumerate": function (oTarget, sKey) {
+    return oTarget.keys();
+  },
+  "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;
+  },
+});
+
+/* Cookies test */
+
+console.log(docCookies.mon_cookie1 = "Première valeur");
+console.log(docCookies.getItem("mon_cookie1"));
+
+docCookies.setItem("mon_cookie1", "Valeur modifiée");
+console.log(docCookies.mon_cookie1);
+ +

Spécifications

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SpécificationÉtatCommentaires
{{SpecName('ES2015', '#sec-proxy-objects', 'Proxy')}}{{Spec2('ES2015')}}Définition initiale.
{{SpecName('ES2016', '#sec-proxy-objects', 'Proxy')}}{{Spec2('ES2016')}}
{{SpecName('ES2017', '#sec-proxy-objects', 'Proxy')}}{{Spec2('ES2017')}}
{{SpecName('ESDraft', '#sec-proxy-objects', 'Proxy')}}{{Spec2('ESDraft')}}
+ +

Compatibilité des navigateurs

+ + + +

{{Compat("javascript.builtins.Proxy", 2)}}

+ +

Voir aussi

+ + + +

Notes de licence

+ +

Certains composants de cette page (texte, exemples) ont été copiés ou adaptés du Wiki ECMAScript dont le contenu est sous licence CC 2.0 BY-NC-SA.

-- cgit v1.2.3-54-g00ecf