Proxy en JavaScript: exemples simples

Outil de simplification du code, le proxy tend à séparer l'essentiel de l'application de l'accessoire.

Les proxies procurent un moyen de contrôler l'accès aux fonctions et objets. Cela simplifie la lecture d'un programme en reléguant tous les contrôles à un objet proxy alors qu'on peut utiliser l'objet de la façon la plus simple possible.

On crée un proxy avec cette instruction:

var p = new Proxy(sujet, interface)

On peut alors substituer le proxy à l'objet ou la fonction dans chaque référence à cet objet ou cette fonction. Chaque trappe intercepte un type d'opération.

Compatibilité

Code du test:

var ptest = new Proxy({}, {})
if(ptest instanceof Object) document.write("Proxy supporté!")

Get

Intercepte la lecture des propriétés d'un objet, ou la valeur de retour d'une fonction.

var d = { "a": 1, "b": 2 }
var pget = new Proxy(
  d,  {
  	get: function(y, idx) {
         return y[idx] * 10
    }  
  }
)

pget.c = 3

for(var z in pget) {
  document.write(z + ":")
	document.write(pget[z])
}
Trappe get

Set

Intercepte l'assignement d'une valeur à une propriété ou l'appel d'une fonction.

var d3 = { "a": 1, "b": 2 }
var pset = new Proxy(
  d3, {
    set: function(y, idx, value) {
    y[idx] = value * 10
   }
  }
)

pset.c = 3

for(var z in pset) {
  document.writez + ":" )
  document.write(pset[z])
}
Trappe set

Has

Modifie la liste des propriétés d'un objet telle qu'elle est vue lors d'un accès à celui-ci, donc la liste des propriétés accessibles. Elle retourne la valeur booléenne true si l'on veut que la propriété soit accessible, ou false autrement, que la clé soit présente ou non dans l'objet originel.

var phas = new Proxy({},  {
has: function(target, key) {
if(key == "a") return false;
return true;
}
}
)
var phasa = ("a" in phas)
var phasb = ("b" in phas)
document.write("a in d: " + phasa)
document.write("b in d: " + phasb)
Trappe has

Apply

Permet d'appeler le proxy avec une liste de paramètres. Intercepte aussi les méthodes apply, call et Reflect.apply lorsque le proxy est déclaré avec une fonction.

Pour mieux comprendre la trappe apply, il convient d'abord de connaître la méthode JavaScript apply.

Cette méthode à pour paramètre une valeur this et un tableau. Le premier paramètre substitue un objet désigné à l'objet qui contient la fonction. Si on appelle la fonction dans une fenêtre, l'objet fenêtre sera remplacé par un autre objet. On passe null pour ne pas changer l'objet contexte.
Le second paramètre est un tableau qui se substitue aux paramètres d'appel de la fonction.

function methodDemo(a, b){
var str = "Objet: " + this + "<br>";
for (var i in methodDemo.arguments) {
str += "argument: " + methodDemo.arguments[i] + "<br>";
}
return str;
}
document.write("Appel direct de la function:");
document.write(methodDemo(10, 20));
document.write("Appel par apply:");
document.write(methodDemo.apply(null, [ "dix", "vingt" ]));
Méthode apply

Cela ne fonctionne qui si l'on accède à la liste des arguments par la propriété arguments. Donc on doit prévoir dans la définition un traitement particulier pour le cas où cette méthode sera utilisée.

C'est différent avec la trappe apply puisque l'on définit une fonction spéciale supplémentaire.

Celle-ci a trois paramètres:

Puis on peut utiliser le proxy de trois façons différentes.

On peut ajouter un paramètre d'object de contexte avec la méthode call ou apply. Le paramètre est null comme ci-dessus quand on reste dans le contexte, et alors les méthodes call et apply sont superflues.

var papp = new Proxy(methodDemo, {
apply: function(methodDemo, ctx, args) {
return methodDemo.apply(ctx, args)
}
})
document.write(papp(100, 200))
Trappe apply

Les arguments passés par le proxy se substituent aux arguments de la méthode apply de methodDemo.

Construct

La trappe construct intercepte les commandes new et Reflect.construct(). Elle retourne un object.

Les paramètres sont:

var pconst = new Proxy(function() {}, {
construct: function(objCible, args, ancienConstructeur) {
return { valeur : args[0] + " à tous" }
}
})
document.write(JSON.stringify(new pconst("Salut "), null, ' '))
Trappe construct

Liste de toutes les trappes de proxy

Selon la spécification ECMAScript 262:

Peut-on remplacer les proxies?

Exemple pour get:

var d2 = { "a": 1, "b": 2 }
function p(x) {
return x * 10
}
d2.c = 3
for(var z in d2) {
document.write(z + ":" )
document.write(p(d2[z]))
}

L'écriture est en fait plus simple sans proxy. Mais ils ne sont évidemment pas fait pour des opérations aussi élémentaires.

Quel est le réel intérêt?

Probablement pas d'intérêt tant que l'on exécute des opérations simples. C'est surtout un outil de clarification du code, qui permet de mettre en second plan les opérations utilitaires comme les contrôles sur les arguments, pour se concentrer sur la logique de l'application. On pourrait en dire autant de la programmation objet si ce n'est que celle-ci facilite aussi la réutilisabilité.

Dans une moindre mesure, le code développé pour les proxies est cependant réutilisable puisque dans la plupart des cas, le proxy applique une fonction à un objet. Cette fonction peut se réutiliser dans de nouveaux proxies.

Quelques exemples d'applications des proxies

© 15 août 2016 Xul.fr