Esta página ha sido traducida del inglés por la comunidad.Aprende más y únete a la comunidad de MDN Web Docs.
Metaprogramación
A partir de ECMAScript 2015, JavaScript gana soporte para los objetosProxy yReflect lo cual te permite interceptar y definir un comportamiento personalizado para las operaciones fundamentales del lenguaje (por ejemplo, búsqueda de propiedades, asignación, enumeración, invocación de funciones, etc.). Con la ayuda de estos dos objetos, puedes programar en el metanivel de JavaScript.
In this article
Proxies
Introducidos en ECMAScript 6, los objetosProxy te permiten interceptar ciertas operaciones e implementar comportamientos personalizados.
Por ejemplo, obtener una propiedad sobre un objeto:
let handler = { get: function (target, name) { return name in target ? target[name] : 42; },};let p = new Proxy({}, handler);p.a = 1;console.log(p.a, p.b); // 1, 42El objetoProxy define untarget (un objeto vacío aquí) y un objetohandler, en el que se implementa ungettrap. Aquí, un objeto que es proxy no devolveráundefined cuando obtenga propiedades indefinidas, sino que devolverá el número42.
Hay ejemplos adicionales disponibles en la página de referenciaProxy.
Terminología
Los siguientes términos se utilizan cuando se habla de la funcionalidad de los proxies.
- handler
Objeto marcador de posición que contiene trampas.
- traps
Los métodos que proporcionan acceso a la propiedad. (Esto es análogo al concepto detrampas en los sistemas operativos).
- target
Objeto que virtualiza el proxy. A menudo se utiliza como interfaz de administración de almacenamiento para el proxy. Las invariantes (semántica que permanece sin cambios) con respecto a la no extensibilidad del objeto o las propiedades no configurables se verifican con el
target.- invariants
La semántica que permanece sin cambios al implementar operaciones personalizadas se denominaninvariants. Si violas las invariantes de un controlador, se lanzará un
TypeError.
Controladores y trampas
La siguiente tabla resume las trampas disponibles para los objetosProxy. Ve laspáginas de referencia para explicaciones detalladas y ejemplos.
| Controlador/Trampa | Intercepciones | Invariantes |
|---|---|---|
handler.getPrototypeOf() | Object.getPrototypeOf()Reflect.getPrototypeOf()__proto__Object.prototype.isPrototypeOf()instanceof |
|
handler.setPrototypeOf() | Object.setPrototypeOf()Reflect.setPrototypeOf() | Sitarget no es extensible, el parámetroprototype debe tener el mismo valor queObject.getPrototypeOf(target). |
handler.isExtensible() | Object.isExtensible()Reflect.isExtensible() | Object.isExtensible(proxy) debe devolver el mismo valor queObject.isExtensible(target). |
handler.preventExtensions() | Object.preventExtensions()Reflect.preventExtensions() | Object.preventExtensions(proxy) solo devuelvetrue siObject.isExtensible(proxy) esfalse. |
handler.getOwnPropertyDescriptor() | Object.getOwnPropertyDescriptor()Reflect.getOwnPropertyDescriptor() |
|
handler.defineProperty() | Object.defineProperty()Reflect.defineProperty() |
|
handler.has() |
|
|
handler.get() |
|
|
handler.set() |
|
|
handler.deleteProperty() |
| Una propiedad no se puede eliminar si existe como una propiedad propia no configurable detarget. |
handler.enumerate() |
| El métodoenumerate debe devolver un objeto. |
handler.ownKeys() | Object.getOwnPropertyNames()Object.getOwnPropertySymbols()Object.keys()Reflect.ownKeys() |
|
handler.apply() | proxy(..args)Function.prototype.apply() yFunction.prototype.call()Reflect.apply() | No hay invariantes para el métodohandler.apply. |
handler.construct() | new proxy(...args)Reflect.construct() | El resultado debe ser unObjeto. |
Proxy revocable
El métodoProxy.revocable() se usa para crear un objetoProxy revocable. Esto significa que el proxy se puede revocar mediante la funciónrevoke y apagar el proxy.
Posteriormente, cualquier operación en el proxy conduce a unTypeError.
let revocable = Proxy.revocable( {}, { get: function (target, name) { return "[[" + name + "]]"; }, },);let proxy = revocable.proxy;console.log(proxy.foo); // "[[foo]]"revocable.revoke();console.log(proxy.foo); // Lanza TypeErrorproxy.foo = 1; // TypeError nuevamentedelete proxy.foo; // todavía TypeErrortypeof proxy; // "object", typeof no activa ninguna trampaReflexión
Reflect es un objeto integrado que proporciona métodos para operaciones JavaScript interceptables. Los métodos son los mismos que los deproxy handlers.
Reflect no es un objeto función.
Reflect ayuda con el reenvío de las operaciones predeterminadas del controlador altarget.
ConReflect.has() por ejemplo, obtienes el operadorin como función:
Reflect.has(Object, "assign"); // trueUna mejor funciónapply
En ES5, normalmente usas el métodoFunction.prototype.apply() para llamar a una función con un valorthis yarguments proporcionado como un arreglo (o unobjeto similar a un arreglo).
Function.prototype.apply.call(Math.floor, undefined, [1.75]);ConReflect.apply esto se vuelve menos detallado y más fácil de entender:
Reflect.apply(Math.floor, undefined, [1.75]);// 1;Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);// "hola"Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index;// 4Reflect.apply("".charAt, "ponies", [3]);// "i"Comprobando si la definición de la propiedad ha sido exitosa
ConObject.defineProperty, que devuelve un objeto si tiene éxito, o arroja unTypeError de lo contrario, usaría un bloquetry...catch para detectar cualquier error que haya ocurrido al definir una propiedad. Debido a queReflect.defineProperty devuelve un estado de éxito booleano, aquí puedes usar un bloqueif...else:
if (Reflect.defineProperty(target, property, attributes)) { // éxito} else { // fracaso}