informatica:programacion:cursos:programacion_avanzada_javascript:proxies
Diferencias
Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
| informatica:programacion:cursos:programacion_avanzada_javascript:proxies [2024/10/28 14:40] – [Ejercicio propuesto] tempwin | informatica:programacion:cursos:programacion_avanzada_javascript:proxies [2024/10/30 16:03] (actual) – [Recursos] tempwin | ||
|---|---|---|---|
| Línea 308: | Línea 308: | ||
| ===== Proxies a objetos de tipo función ===== | ===== Proxies a objetos de tipo función ===== | ||
| + | Ya hemos visto cómo con el método '' | ||
| + | |||
| + | Y es que, en JavaScript, las funciones son un tipo más de objeto, así que crear un proxy a una función no debe resultarte sorprendente. | ||
| + | |||
| + | Con un proxy a una función podemos interceptar todas las llamadas a dicha función y ejecutar código antes y/o después de ejecutar la función envuelta por el proxy (aunque también podríamos optar por ni tan siquiera ejecutarla y devolver cualquier otro valor). | ||
| + | |||
| + | Para crear un proxy a función en el handler se define el método apply. Este método recibe los siguientes parámetros: | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | Veamos un ejemplo: | ||
| + | |||
| + | <code javascript> | ||
| + | let foo = function(...values) { | ||
| + | let result = 0; | ||
| + | values.forEach(s => result += s); | ||
| + | console.log(' | ||
| + | } | ||
| + | |||
| + | let proxy = new Proxy(foo, { | ||
| + | apply(target, | ||
| + | let args = argumentsList.map(arg => typeof(arg) === " | ||
| + | return target.apply(thisArg, | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | proxy(10, | ||
| + | </ | ||
| + | |||
| + | El proxy intercepta todas las llamadas a '' | ||
| + | |||
| + | ==== Interceptando call, apply y bind ==== | ||
| + | |||
| + | El uso de '' | ||
| + | |||
| + | <code javascript> | ||
| + | proxy.apply([10], | ||
| + | proxy.call([20], | ||
| + | </ | ||
| + | |||
| + | En ambos casos se intercepta la llamada (se ejecuta el método '' | ||
| + | |||
| + | Del mismo modo dado un proxy a una función, si usamos '' | ||
| + | |||
| + | <code javascript> | ||
| + | let proxy2 = proxy.bind(" | ||
| + | proxy(10, 20, " | ||
| + | </ | ||
| + | |||
| + | En resumen, con los proxies podemos interceptar llamadas a funciones de forma sencilla. | ||
| + | |||
| + | Las posibilidades son muy amplias: validación de precondiciones y/o postcondiciones, | ||
| + | |||
| + | Vamos a ver a continuación un caso práctico de uso. | ||
| ===== DEMO: Creando un " | ===== DEMO: Creando un " | ||
| + | Vamos a tener un objeto que nos permitirá generar proxies de objetos y poder congelar el objeto, que las llamadas a los //setters// no sean válidas. | ||
| + | |||
| + | Empezaremos creando una clase que utilizaremos para obtener los proxies de un determinado objeto: | ||
| + | |||
| + | <code javascript> | ||
| + | // Utilizaremos un WeakMap para guardar las variables privadas | ||
| + | // de los objetos de tipo Freezer | ||
| + | const _privates = new WeakMap(); | ||
| + | |||
| + | class Freezer { | ||
| + | |||
| + | constructor(obj) { | ||
| + | let pr = {}; | ||
| + | | ||
| + | | ||
| + | | ||
| + | } | ||
| + | | ||
| + | // Método para " | ||
| + | frozen(value) { | ||
| + | let pr = _privates.get(this); | ||
| + | pr.frozen = value ? true : false; | ||
| + | } | ||
| + | | ||
| + | get value() { | ||
| + | let pr = _privates.get(this); | ||
| + | return pr.proxy; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Vamos a utilizarla: | ||
| + | |||
| + | <code javascript> | ||
| + | var obj = {v: 42}; | ||
| + | |||
| + | var freezer = new Freezer(obj); | ||
| + | var proxy = freezer.value(); | ||
| + | |||
| + | obj.v = 100; | ||
| + | proxy; // Object { v: 100 } | ||
| + | </ | ||
| + | |||
| + | Ahora tendremos que añadir la funcionalidad para congelar el objeto. | ||
| ===== DEMO: Creando un " | ===== DEMO: Creando un " | ||
| + | <code javascript> | ||
| + | // Utilizaremos un WeakMap para guardar las variables privadas | ||
| + | // de los objetos de tipo Freezer | ||
| + | const _privates = new WeakMap(); | ||
| + | |||
| + | const createConfig = function(pr) { | ||
| + | return { | ||
| + | set (target, key, receiver) { | ||
| + | var prop = target[key]; | ||
| + | if (pr.frozen) { | ||
| + | return prop. | ||
| + | } else { | ||
| + | target[key] = receiver; | ||
| + | return receiver; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | class Freezer { | ||
| + | |||
| + | constructor(obj) { | ||
| + | let pr = {}; | ||
| + | | ||
| + | | ||
| + | | ||
| + | } | ||
| + | |||
| + | // Método para " | ||
| + | frozen(value) { | ||
| + | let pr = _privates.get(this); | ||
| + | pr.frozen = value ? true : false; | ||
| + | } | ||
| + | |||
| + | get value() { | ||
| + | let pr = _privates.get(this); | ||
| + | return pr.proxy; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Probamos: | ||
| + | |||
| + | <code javascript> | ||
| + | var obj = {v:42}; | ||
| + | |||
| + | var freezer = new Freezer(obj); | ||
| + | var proxy = freezer.value; | ||
| + | |||
| + | proxy; // Object { v: 42 } | ||
| + | |||
| + | // Congelamos el objeto: | ||
| + | freezer.frozen(true); | ||
| + | |||
| + | // Comprobemos que realmente está congelado: | ||
| + | |||
| + | prop.v = 100; | ||
| + | |||
| + | proxy.v; // 42 | ||
| + | |||
| + | // Para descongelarlo: | ||
| + | freezer.frozen(false); | ||
| + | |||
| + | prop.v = 200; | ||
| + | proxy.v; // 200 | ||
| + | </ | ||
| ===== DEMO: Creando un " | ===== DEMO: Creando un " | ||
| + | Después de lo anterior, vamos a agregar un método ficticio al proxy que llamándolo nos permita congelar el propio objeto. | ||
| + | |||
| + | Queremos lograr hacer algo como '' | ||
| + | |||
| + | Vamos a ello: | ||
| + | |||
| + | <code javascript> | ||
| + | // Utilizaremos un WeakMap para guardar las variables privadas | ||
| + | // de los objetos de tipo Freezer | ||
| + | const _privates = new WeakMap(); | ||
| + | |||
| + | // Para asegurarnos de que el nombre del método que crearemos | ||
| + | // en el proxy es único y no tenga conflictos, utilizaremos un símbolo | ||
| + | const _freeze = Symbol(" | ||
| + | |||
| + | const createConfig = function(pr) { | ||
| + | return { | ||
| + | set (target, key, receiver) { | ||
| + | var prop = target[key]; | ||
| + | if (pr.frozen) { | ||
| + | return prop. | ||
| + | } else { | ||
| + | target[key] = receiver; | ||
| + | return receiver; | ||
| + | } | ||
| + | }, | ||
| + | get (target, key, receiver) { | ||
| + | if (key === _freeze) { | ||
| + | return function(v) { | ||
| + | pr.frozen = v ? true : false; | ||
| + | return pr.frozen; | ||
| + | } | ||
| + | } else { | ||
| + | return target[key]; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | class Freezer { | ||
| + | |||
| + | constructor(obj) { | ||
| + | let pr = {}; | ||
| + | | ||
| + | | ||
| + | | ||
| + | } | ||
| + | |||
| + | // Método para " | ||
| + | frozen(value) { | ||
| + | let pr = _privates.get(this); | ||
| + | pr.frozen = value ? true : false; | ||
| + | } | ||
| + | |||
| + | get value() { | ||
| + | let pr = _privates.get(this); | ||
| + | return pr.proxy; | ||
| + | } | ||
| + | | ||
| + | // getter para obtener el símbolo | ||
| + | static get freeze() { | ||
| + | return _freeze; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Probamos: | ||
| + | |||
| + | <code javascript> | ||
| + | var obj = {v:42}; | ||
| + | |||
| + | var freezer = new Freezer(obj); | ||
| + | var proxy = freezer.value; | ||
| + | |||
| + | // Congelamos: | ||
| + | proxy[Freezer.freeze](true); | ||
| + | |||
| + | proxy.v = 100; | ||
| + | |||
| + | proxy,.v; // 42 | ||
| + | |||
| + | obj.v; // 42 | ||
| + | |||
| + | // Descongelamos: | ||
| + | freezer.frozen(false); | ||
| + | |||
| + | proxy.v = 100; | ||
| + | |||
| + | proxy.v; // 100 | ||
| + | obj.v; // 100 | ||
| + | </ | ||
| + | |||
| + | ===== Recursos ===== | ||
| + | |||
| + | * [[https:// | ||
| + | * [[https:// | ||
| + | * [[https:// | ||
informatica/programacion/cursos/programacion_avanzada_javascript/proxies.1730122855.txt.gz · Última modificación: por tempwin
