Introducción a la programación funcional en JavaScript
La programación funcional (PF) es un paradigma de programación en el que los cálculos se consideran como la aplicación de funciones matemáticas. A diferencia del enfoque imperativo, donde escribimos una secuencia de instrucciones que modifican el estado del programa, el estilo funcional se centra en qué se debe calcular, no en cómo hacerlo.
JavaScript, al ser un lenguaje multiparadigma, es excelente para la programación funcional. En el entorno Node.js, este enfoque es especialmente útil al procesar datos, trabajar con flujos y construir aplicaciones de servidor robustas. En este artículo, analizaremos los conceptos clave de la PF con ejemplos prácticos.
Principios fundamentales de la programación funcional
Funciones puras (Pure Functions)
Una función pura es una función que:
- Siempre devuelve el mismo resultado para los mismos argumentos (determinismo).
- No tiene efectos secundarios (no modifica variables externas, no imprime en consola, no escribe en archivos).
// Función puraconst add = (a, b) => a + b;
// Función impura (efecto secundario — salida por consola)const impureAdd = (a, b) => { console.log(`Sumando ${a} y ${b}`); return a + b;};
// Función impura (depende del estado externo)let counter = 0;const increment = () => ++counter;Ventajas de las funciones puras: son fáciles de probar, depurar y paralelizar. Son predecibles y no dependen del contexto de ejecución.
Inmutabilidad (Immutability)
La inmutabilidad significa que los datos, una vez creados, no se modifican. En lugar de modificar un objeto existente, creamos una nueva copia del mismo con los cambios necesarios.
// Enfoque mutable (malo)const user = { name: 'Iván', age: 30 };user.age = 31; // Modificamos directamente
// Enfoque inmutable (bueno)const user2 = { name: 'María', age: 25 };const updatedUser = { ...user2, age: 26 };// El objeto original user2 permanece sin cambios
// Trabajando con arraysconst numbers = [1, 2, 3];const newNumbers = [...numbers, 4]; // [1, 2, 3, 4]// numbers sigue siendo [1, 2, 3]En Node.js, la inmutabilidad es especialmente importante al trabajar con solicitudes y el estado de la aplicación, para evitar mutaciones inesperadas en el código asíncrono.
Funciones de orden superior y closures
Funciones de orden superior (Higher-Order Functions)
Son funciones que reciben otras funciones como argumentos o las devuelven. Los métodos incorporados de arrays map, filter, reduce son ejemplos clásicos.
const numbers = [1, 2, 3, 4, 5];
// map — transforma cada elementoconst doubled = numbers.map(n => n * 2);// [2, 4, 6, 8, 10]
// filter — conserva los elementos que cumplen la condiciónconst evens = numbers.filter(n => n % 2 === 0);// [2, 4]
// reduce — reduce el array a un solo valorconst sum = numbers.reduce((acc, n) => acc + n, 0);// 15Closures
Un closure es una función que «recuerda» las variables del ámbito en el que fue creada, incluso después de que ese ámbito haya terminado su ejecución.
const createCounter = () => { let count = 0; // Variable privada return () => ++count;};
const counter1 = createCounter();console.log(counter1()); // 1console.log(counter1()); // 2
const counter2 = createCounter();console.log(counter2()); // 1 (contador independiente)Los closures se utilizan ampliamente para crear fábricas de funciones y encapsular el estado en el estilo funcional.
Currying y composición de funciones
Currying
El currying es la transformación de una función que recibe múltiples argumentos en una secuencia de funciones, cada una de las cuales