Interceptez les opérations fondamentales du langage avec les trappes de Proxy et déléguez proprement le comportement par défaut grâce à Reflect.
Ouvrir cette leçon dans KodokonUn Proxy enveloppe un objet cible et intercepte les opérations internes du langage via treize trappes : get, set, has (l'opérateur in), deleteProperty, ownKeys, apply, construct, etc. L'objet Reflect expose exactement les mêmes opérations sous forme de fonctions, avec les mêmes signatures que les trappes. C'est la fondation de la réactivité de Vue 3, des mocks dynamiques et des API de type ORM.
const target = { name: "Ada" };
const traced = new Proxy(target, {
get(obj, prop, receiver) {
console.log("get " + String(prop));
return Reflect.get(obj, prop, receiver);
}
});
traced.name; // get namePourquoi Reflect.get(obj, prop, receiver) plutôt que obj[prop] ? À cause du receiver. Si la propriété est un accesseur (getter), Reflect.get l'invoque avec this pointant vers le receiver - le proxy ou l'objet qui en hérite - alors que obj[prop] fixerait this sur la cible brute. Sans receiver, les getters qui lisent this cassent dès qu'un objet hérite du proxy.
const base = {
get label() {
return this.name;
}
};
const proxy = new Proxy(base, {
get(target, prop, receiver) {
return Reflect.get(target, prop, receiver);
}
});
const child = Object.create(proxy);
child.name = "Ada";
console.log(child.label); // "Ada" grace au receiverLes cas d'usage sérieux : validation à l'écriture, propriétés virtuelles calculées à la volée, journalisation, contrôle d'accès (masquer les clés préfixées par _ via has et ownKeys), ou objets à comportement par défaut. Notez que Reflect.set renvoie un booléen au lieu de lever une erreur en mode strict : votre trappe doit propager cette valeur de retour.
function validated(target) {
return new Proxy(target, {
set(obj, prop, value, receiver) {
if (prop === "age" && !Number.isInteger(value)) {
throw new TypeError("age must be an integer");
}
return Reflect.set(obj, prop, value, receiver);
}
});
}
const user = validated({ age: 30 });
user.age = 31; // ok
// user.age = "old"; // TypeErrorget, pourquoi préférer Reflect.get(target, prop, receiver) à target[prop] ?this (le receiver) et non la cible brutetarget[prop] relancerait la trappe et créerait une récursion infinie"id" in obj ?get renvoie 42 pour une propriété de la cible qui vaut 1, déclarée non-configurable et non-writable. Que se passe-t-il ?