Command Palette

Search for a command to run...

Funciones Puras en JavaScript: Código Predecible y Testeable

Aprende qué son las funciones puras, sus características principales y cómo escribirlas para crear código más mantenible, testeable y sin efectos secundarios.

Lectura: 12 min
Nivel: Intermedio

TL;DR - Resumen rápido

  • Las funciones puras siempre retornan el mismo resultado para los mismos argumentos
  • No modifican variables externas ni producen efectos secundarios
  • Son fácilmente testeables y predecibles
  • Facilitan el debugging y el razonamiento sobre el código
  • Son fundamentales para la programación funcional y paradigmas modernos

Introducción a las Funciones Puras

Las funciones puras son uno de los conceptos más importantes de la programación funcional y un pilar fundamental para escribir código de calidad en JavaScript. Una función pura es aquella que, dados los mismos argumentos, siempre retorna el mismo resultado y no produce efectos secundarios. Esta simplicidad aparente tiene implicaciones profundas en la calidad, mantenibilidad y testeabilidad de tu código.

En el ecosistema de JavaScript moderno, las funciones puras son esenciales para frameworks como React, Redux y bibliotecas de procesamiento de datos. Comprender cómo escribir funciones puras te permitirá crear código más robusto, fácil de entender y menos propenso a bugs. A diferencia de las funciones impuras que pueden modificar el estado global o depender de factores externos, las funciones puras son completamente predecibles y deterministas.

Contexto Histórico

El concepto de funciones puras proviene de la programación funcional, un paradigma que tiene sus raíces en el cálculo lambda de Alonzo Church (década de 1930). Aunque JavaScript no es un lenguaje puramente funcional, su naturaleza como lenguaje de primera clase para funciones lo hace ideal para adoptar estos principios.

¿Qué son las Funciones Puras?

Una función pura cumple con dos requisitos fundamentales: determinismo y ausencia de efectos secundarios. El determinismo significa que la función siempre produce el mismo resultado cuando se le proporcionan los mismos argumentos. La ausencia de efectos secundarios implica que la función no modifica variables externas, no realiza operaciones de I/O, no lanza excepciones (salvo las que son parte del contrato de la función) y no altera el estado global del programa.

Características Principales

Para que una función sea considerada pura, debe cumplir con estas características esenciales que garantizan su comportamiento predecible y libre de efectos secundarios.

  • <strong>Determinismo:</strong> Mismo resultado para los mismos argumentos siempre
  • <strong>Sin efectos secundarios:</strong> No modifica variables externas ni estado global
  • <strong>Independencia:</strong> No depende de variables externas o estado mutable
  • <strong>Transparencia referencial:</strong> Puede ser reemplazada por su resultado sin afectar el programa
  • <strong>Inmutabilidad:</strong> No muta los argumentos que recibe
caracteristicas-pura.js
Loading code...

Este ejemplo muestra una función pura que calcula el área de un círculo. La función es determinista porque, dado el mismo radio, siempre retorna el mismo resultado. No tiene efectos secundarios porque no modifica ninguna variable externa ni depende de estado mutable. La transparencia referencial significa que podrías reemplazar cualquier llamada a esta función con su resultado calculado y el programa funcionaría exactamente igual.

Ejemplos de Funciones Puras

Las funciones puras son comunes en JavaScript, especialmente en operaciones matemáticas, transformaciones de datos y algoritmos que no requieren interacción con el mundo exterior.

ejemplos-puras.js
Loading code...

Estos ejemplos demuestran diferentes tipos de funciones puras. La función sumar es un caso simple de operación matemática. La función filtrarMayoresDe es una función de orden superior que retorna un nuevo array sin modificar el original, respetando el principio de inmutabilidad. La función obtenerPropiedad accede a una propiedad de un objeto de forma segura, sin modificar el objeto original. Todas estas funciones son predecibles y pueden ser testeadas fácilmente.

Mejor Práctica

Cuando trabajes con arrays y objetos, usa métodos como map(), filter(), reduce() y el spread operator (...) para crear nuevas estructuras en lugar de mutar las existentes. Esto mantiene tus funciones puras y evita bugs difíciles de rastrear.

Funciones Impuras: Qué Evitar

Las funciones impuras son el opuesto de las funciones puras. Pueden modificar variables externas, depender del estado global, realizar operaciones de I/O o generar resultados no deterministas. Identificar y evitar funciones impuras es crucial para escribir código mantenible y predecible.

funciones-impuras.js
Loading code...

Estos ejemplos muestran patrones comunes de funciones impuras. La primera función modifica una variable externa, lo que puede causar bugs difíciles de rastrear. La segunda depende de Math.random(), que produce resultados no deterministas. La tercera función muta el array original en lugar de crear uno nuevo. Estas prácticas hacen el código más difícil de testear, depurar y mantener.

Advertencia Crítica

Las funciones que mutan sus argumentos o dependen de estado externo son una fuente común de bugs en JavaScript. Especialmente en aplicaciones con múltiples componentes o en entornos asíncronos, los efectos secundarios pueden causar condiciones de carrera y comportamientos inesperados que son extremadamente difíciles de depurar.

refactorizar-impura.js
Loading code...

Aquí mostramos cómo refactorizar funciones impuras para hacerlas puras. La versión original de agregarUsuario mutaba el array original, mientras que la versión pura retorna un nuevo array. La función obtenerUsuario ahora recibe el ID como argumento en lugar de depender de una variable externa. Este patrón hace el código más predecible y fácil de testear.

Beneficios de las Funciones Puras

Adoptar funciones puras en tu código JavaScript proporciona múltiples beneficios que mejoran significativamente la calidad del software. Estos beneficios se hacen más evidentes a medida que el código base crece en complejidad y tamaño.

  • <strong>Testeabilidad:</strong> Son fáciles de probar unitariamente sin necesidad de mocks complejos
  • <strong>Predecibilidad:</strong> El comportamiento es determinista y fácil de entender
  • <strong>Debugging:</strong> Los bugs son más fáciles de rastrear y reproducir
  • <strong>Paralelización:</strong> Pueden ejecutarse en paralelo sin problemas de concurrencia
  • <strong>Reutilización:</strong> Son más fáciles de reutilizar en diferentes contextos
testeo-funcion-pura.js
Loading code...

Este ejemplo demuestra cómo las funciones puras facilitan el testing. La función calcularImpuesto es pura, por lo que podemos probarla simplemente verificando que retorna el valor esperado para un conjunto de entradas. No necesitamos configurar estado global, mockear dependencias o preocuparnos por efectos secundarios. Esto hace que las pruebas sean más rápidas, más confiables y más fáciles de mantener.

Patrones Comunes en JavaScript

JavaScript proporciona varias características y patrones que facilitan la escritura de funciones puras. Conocer estos patrones te ayudará a escribir código más funcional y menos propenso a errores.

patrones-puros.js
Loading code...

Este ejemplo muestra patrones comunes para escribir funciones puras en JavaScript. El uso de const para variables que no cambian, el spread operator para crear copias de objetos y arrays, y los métodos de array que no mutan (map, filter, reduce) son fundamentales. El currying es un patrón avanzado que permite crear funciones más especializadas a partir de funciones más generales, manteniendo la pureza.

Currying y Composición

El currying es una técnica donde una función que toma múltiples argumentos se transforma en una secuencia de funciones que toman un solo argumento. Esto permite la composición de funciones, donde el resultado de una función se pasa como argumento a otra. La composición de funciones puras es un patrón poderoso para crear pipelines de transformación de datos.

Errores Comunes

Al escribir funciones puras, es fácil cometer errores sutiles que pueden introducir impurezas no intencionales. Estos son los errores más frecuentes que debes evitar para mantener tus funciones verdaderamente puras.

errores-comunes.js
Loading code...

Este ejemplo muestra errores comunes que pueden hacer que una función parezca pura pero no lo sea. El primer error es mutar objetos dentro de la función, incluso si el objeto es un argumento. El segundo error es usar closures que capturan variables externas mutables. El tercer error es depender de la fecha/hora actual, que hace que la función sea no determinista. Estos patrones son especialmente peligrosos porque pueden pasar desapercibidos durante el desarrollo y causar bugs en producción.

Error Crítico

Nunca uses console.log, fetch, localStorage u otras operaciones de I/O dentro de funciones que deben ser puras. Estas operaciones son efectos secundarios por definición y rompen la pureza de la función. Si necesitas realizar estas operaciones, sepáralas de la lógica pura en funciones distintas.

Resumen: Funciones Puras en JavaScript

Conceptos principales:

  • Las funciones puras siempre retornan el mismo resultado para los mismos argumentos
  • No producen efectos secundarios ni modifican estado externo
  • Son deterministas y predecibles por naturaleza
  • Cumplen con el principio de transparencia referencial
  • No mutan los argumentos que reciben, crean nuevas estructuras cuando es necesario

Mejores prácticas:

  • Usa métodos de array inmutables como map, filter y reduce
  • Evita modificar objetos y arrays directamente, usa spread operator
  • Separa la lógica pura de las operaciones de I/O y efectos secundarios
  • Escribe funciones pequeñas y enfocadas en una sola responsabilidad
  • Preferir const sobre let para variables que no cambian