Luis Tavarez · Noticias

JavaScript: imperativo, funcional y una sola pasada

Resuelve el mismo total con tres estilos. Verifica resultados, entrada vacía e inmutabilidad antes de discutir preferencias de programación.

Luis Tavarez ·

El problema antes del paradigma

Queremos sumar los centavos de órdenes pagadas sin modificar las órdenes recibidas. Un bucle con acumulador, una composición filter/map/reduce y un reduce de una sola pasada satisfacen el mismo contrato en este conjunto de datos.

Tres recorridos explícitos

El bucle describe cómo avanza el cálculo. La composición describe transformaciones separadas y crea arrays intermedios. El reduce de una pasada combina selección y acumulación. Ninguno necesita un estado global ni efectos externos.

Una variable local mutable no hace impura a una función: el criterio relevante es lo que el llamador puede observar. Object.freeze se aplica aquí tanto al array como a sus objetos; congelar solo el array no congelaría sus elementos. Tampoco equivale a congelar recursivamente un árbol arbitrario.

Verificar antes de comparar

Las tres funciones deben producir 2000 para el conjunto de prueba y cero para una entrada vacía. La aserción final confirma que los importes de entrada siguen iguales. El ejemplo no ejecuta un benchmark y no afirma que un estilo sea más rápido.

Elegir según el equipo y los datos

Usa la forma que permita revisar mejor el contrato y los efectos. Si el volumen hace relevantes las asignaciones intermedias, mide con datos representativos. Number no representa todos los enteros: importes o sumas fuera del rango seguro necesitan validación o una representación adecuada. Esta guía usa enteros pequeños, no un motor financiero.

JavaScript · Laboratorio para desarrolladores

Arquitectura, código y práctica

LT Architecture StudioDiagrama creado con nuestra herramienta. Explóralo y adáptalo a tu proyecto.
JavaScript: imperativo, funcional y una sola pasada

Ejecutar el ejemplo

Node.js 22 o superior. Descarga main.mjs y ejecútalo sin dependencias externas.

node main.mjs

Salida esperada: OK: three styles, same result, unchanged input

Código del ejemplo
javascript
import assert from 'node:assert/strict';

const orders = Object.freeze([
  Object.freeze({ cents: 1200, paid: true }),
  Object.freeze({ cents: 700, paid: false }),
  Object.freeze({ cents: 800, paid: true }),
]);

function imperativeTotal(values) {
  let total = 0;
  for (const order of values) {
    if (order.paid) total += order.cents;
  }
  return total;
}

const functionalTotal = values => values
  .filter(order => order.paid)
  .map(order => order.cents)
  .reduce((total, cents) => total + cents, 0);

// A pure function can also use a local accumulator: callers cannot observe it.
const singlePassTotal = values => values.reduce(
  (total, order) => total + (order.paid ? order.cents : 0), 0,
);

for (const total of [imperativeTotal, functionalTotal, singlePassTotal]) {
  assert.equal(total(orders), 2000);
  assert.equal(total([]), 0);
}
assert.deepEqual(orders.map(order => order.cents), [1200, 700, 800]);
console.log('OK: three styles, same result, unchanged input');

Ejercicio para continuar

Añade una política de descuentos como función pura y conserva las tres aserciones. Después decide qué efectos deben quedar fuera del cálculo.