Decoradores (No nativos de JS, sí nativos en TS)
Los decoradores son una propuesta avanzada de JavaScript (Stage 3 en TC39) que permite extender las clases, métodos y propiedades.
Decoradores
Los decoradores son una propuesta avanzada de JavaScript (Stage 3 en TC39). Para usarlos, necesitamos configurar nuestro entorno con herramientas como Babel o usando TypeScript. No están disponibles en entornos JS "puros" como navegadores sin configuración especial.
Los decoradores son funciones que nos permiten extender las clases añadiendo, modificando o incluso reemplazando sus funcionalidades. Los decoradores afectan a las clases y también a sus métodos y propiedades.
Se definen con una arroba @ al principio.
Aclaración: El patrón decorador (como patrón de diseño) no es lo mismo que la funcionalidad formal de los decoradores en el lenguaje. Si bien podemos implementar el patrón decorador usando la sintaxis de decoradores, se pueden lograr objetivos similares sin ellos.
Ejemplo básico
function logger(value, context) {
console.log(value, context);
}
@logger // kind = class
class Persona {
@logger
weight = 75; // kind = field
@logger
getWeight() { return this.weight; } // kind = method
@logger
get peso() { return this.weight; } // kind = getter
@logger
set peso(value) { this.weight = value; } // kind = setter
}En este ejemplo, aplicamos el decorador @logger en varios elementos. La función logger() se ejecutará varias veces, una por cada elemento asociado.
Cuando el motor lee nuestros elementos (clase, propiedades, métodos), ejecuta la función asociada al decorador. La función logger recibe dos parámetros:
value: Contiene el elemento que estamos decorando.context: Recibe un objeto con metadatos del elemento decorado (su tipo, nombre, si es estático, privado, etc.).
El objeto Context
function logger(value, context) {
console.log(context.kind); // "class", "method", "field", "getter", "setter"
console.log(context.name); // Nombre del método o propiedad
console.log(context.static); // true si es static
console.log(context.private); // true si es private
}Orden de ejecución de los decoradores:
- Elementos de tipo
field - Elementos de tipo
setter - Elementos de tipo
getter - Elementos de tipo
method - Elementos de tipo
class
Extendiendo Funcionalidad
Podemos usar decoradores para envolver métodos y añadir lógica extra, como logs o validaciones.
function logger(value, { name, kind }) {
if (kind === "method") {
return function (...args) {
console.log(`Log: Ejecutando ${name} con argumentos: ${args.join(", ")}`);
const returnedValue = value.call(this, ...args);
console.log(`Log: Fin de ejecución. Retornó: ${returnedValue}`);
return returnedValue;
}
}
}Aquí usamos la desestructuración { name, kind } para obtener solo lo que necesitamos del contexto. Si el elemento es un método, retornamos una nueva función que envuelve a la original (value). Usamos .call(this, ...args) para asegurar que el contexto de la clase se mantenga correctamente.
Casos de uso prácticos
La verdadera utilidad de los decoradores es añadir comportamientos transversales (cross-cutting concerns) sin ensuciar la lógica de negocio de la clase. Por ejemplo, medir el tiempo de ejecución:
Decorador @trackExecution
function trackExecution(value, { name }) {
return function (...args) {
console.time(`⏱ ${name}`);
const result = value.call(this, ...args);
console.timeEnd(`⏱ ${name}`);
return result;
};
}
class Calculadora {
@trackExecution
sumar(a, b) {
return a + b;
}
}
const calc = new Calculadora();
calc.sumar(4, 7); // La consola mostrará el tiempo que tardó en ejecutarseHistory API (Navegar hacia atrás, adelante, etc)
Cómo interactuar con el historial del navegador mediante la History API, esencial para la navegación en Single Page Applications (SPA).
Introducción a JavaScript
JavaScript es un lenguaje de programación interpretado, dinámico, de alto nivel y de tipado débil.