JavaScript: Decoradores y reenvío de funciones
Los decoradores de JavaScript y los métodos call/apply son esenciales para gestionar el contexto de las funciones y extender el comportamiento de las clases. Esta guía explica cómo usarlos de manera efectiva con ejemplos prácticos.
Comprensión de los decoradores de JavaScript
Los decoradores ofrecen una forma dinámica de observar, modificar o reemplazar declaraciones y miembros de clases. La propuesta de decoradores se encuentra actualmente en la Etapa 3. Aunque la sintaxis moderna está estandarizada, muchas bases de código y tutoriales existentes aún utilizan la sintaxis heredada, que requiere un transpilador como Babel.
¿Qué son los decoradores?
En esencia, los decoradores son funciones que te permiten agregar nueva funcionalidad a elementos de clase (métodos, accesores, propiedades) de manera declarativa. En tiempo de ejecución, envuelven el método o propiedad original, interceptando las llamadas o alterando su configuración antes de que se instancie la clase. Se aplican directamente antes de la definición del elemento de clase, proporcionando una forma sintáctica y expresiva de modificar el comportamiento.
Uso de decoradores
Para usar un decorador, prefijas la función decoradora con el símbolo @ y la colocas encima del elemento de clase que deseas modificar. Aquí tienes un ejemplo básico:
Advertencia: La sintaxis de decoradores heredada a continuación está en desuso y requiere un transpilador. La sintaxis moderna de la Etapa 3 está estandarizada y se recomienda para nuevos proyectos.
function log(target, name, descriptor) {
const original = descriptor.value;
if (typeof original === 'function') {
descriptor.value = function(...args) {
console.log(`Calling ${name} with`, args);
return original.apply(this, args);
}
}
return descriptor;
}
class MyClass {
@log
doSomething(arg) {
// Method body
}
}Este decorador log mostrará un mensaje en la consola cada vez que se llame al método doSomething, incluyendo los argumentos pasados.
Aquí tienes el equivalente moderno de la Etapa 3. En la especificación de la Etapa 3, target para decoradores de métodos se refiere a la función del método original en el prototipo, por lo que original.apply(this, args) lo invoca correctamente. Devolver una nueva función reemplaza al método original. Los decoradores también se pueden aplicar a campos y accesores de clase, aunque su inicialización y manejo de descriptores difieren ligeramente según el tipo de elemento.
function log(target, context) {
const original = target;
return function(...args) {
console.log(`Calling ${context.name} with`, args);
return original.apply(this, args);
};
}
class MyClass {
@log
doSomething(arg) {
// Method body
}
}Aprovechamiento de call y apply
Los métodos call y apply son herramientas esenciales en JavaScript para especificar el contexto this de una función e invocarla. Aunque cumplen propósitos similares, su uso difiere ligeramente.
El método call
El método call invoca una función con un valor this dado y los argumentos proporcionados individualmente.
Ejemplo de call:
El método apply
Por el contrario, apply invoca la función con un valor this dado y los argumentos proporcionados como un array.
Ejemplo de apply:
Tanto call como apply son poderosos en escenarios donde el contexto de this debe definirse explícitamente o al trabajar con funciones que aceptan un número variable de argumentos.
Nota: A diferencia de
callyapply, que invocan la función inmediatamente,binddevuelve una nueva función con el contextothisestablecido permanentemente, lo que permite una ejecución diferida.
const boundGreet = greet.bind(person);
boundGreet(); // Output: "Hello, John"Aplicaciones prácticas
Los decoradores simplifican la adición de comportamiento a objetos sin modificar el código original, fomentando un diseño más limpio y modular. De manera similar, call y apply ofrecen flexibilidad en la invocación de funciones, lo cual es crucial para mantener el contexto this correcto o manejar funciones con número variable de argumentos.
Conclusión
Los decoradores y los métodos call/apply proporcionan herramientas poderosas para escribir JavaScript conciso, flexible y eficiente. Dominar estos conceptos permite una mejor reutilización de código, abstracción y gestión de contexto en aplicaciones complejas.
Práctica
¿Cuáles de las siguientes afirmaciones describen con precisión el uso y las diferencias entre los métodos `call` y `apply` en JavaScript?