Lección 9 · 9 de 20
Orden superior y composición · JavaScript
Cree en JavaScript funciones que reciben y devuelven funciones: pipe, compose, envoltorios y reglas combinables para sus pedidos.

Una función de orden superior recibe funciones, devuelve funciones o ambas cosas. Ya conoce map o filter; aquí se crean las propias. pipe recibe varias funciones y devuelve una nueva que pasa el valor por todas, de izquierda a derecha, con reduce.
La composición consiste en construir una tarea grande a partir de pasos pequeños con una sola responsabilidad. cleanUsername encadena trim, lower y dots, y convierte " Ana Maria Ruiz " en ana.maria.ruiz. compose hace lo mismo pero de derecha a izquierda, como en matemáticas: en shout, primero se pasa a mayúsculas y después se añade la exclamación.
withLog es un envoltorio (o decorador): recibe una función y devuelve otra que hace lo mismo y, además, escribe sus argumentos y su resultado. Gracias a ...args funciona con cualquier número de parámetros. Al componer addVat y coupon, la salida muestra cada paso del cálculo antes del total.
Las funciones también son datos: every recibe una lista de reglas y devuelve un único predicado que las exige todas. freeShipping se pasa tal cual a filter, y solo el pedido 1 cumple las dos condiciones. Este estilo es muy habitual en React, uno de los destinos de DMarlon Studio que se programan en JavaScript.
Código · main.js
// pipe and compose take functions and return a new function.const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x);// Small single-purpose steps for cleaning a username.const trim = (s) => s.trim();const lower = (s) => s.toLowerCase();const dots = (s) => s.split(" ").join(".");const cleanUsername = pipe(trim, lower, dots);console.log(cleanUsername(" Ana Maria Ruiz "));// compose reads right to left: the last function runs first.const shout = compose((s) => s + "!", (s) => s.toUpperCase());console.log(shout("order shipped"));// A function that wraps another one to add logging.function withLog(name, fn) { return (...args) => { const result = fn(...args); console.log(` ${name}(${args.join(", ")}) = ${result}`); return result; };}const addVat = withLog("addVat", (net) => +(net * 1.21).toFixed(2));const coupon = withLog("coupon", (price) => price - 5);console.log("Checkout:", pipe(addVat, coupon)(100));// Functions as data: a list of rules combined into one test.const every = (...rules) => (item) => rules.every((rule) => rule(item));const freeShipping = every((o) => o.total > 50, (o) => o.country === "ES");const orders = [{ id: 1, total: 80, country: "ES" }, { id: 2, total: 30, country: "ES" }, { id: 3, total: 90, country: "FR" }];console.log("Free shipping:", orders.filter(freeShipping).map((o) => `#${o.id}`).join(", "));Salida · node main.js
ana.maria.ruiz ORDER SHIPPED! addVat(100) = 121 coupon(121) = 116 Checkout: 116 Free shipping: #1
Para recordar
- Una función de orden superior recibe o devuelve funciones.
pipeaplica las funciones de izquierda a derecha;compose, de derecha a izquierda.- Un envoltorio añade comportamiento sin cambiar la función original.
- Las reglas en forma de función se pueden combinar como datos.
Ejercicio
Añada una regla más a freeShipping (por ejemplo, que el pedido no supere 10 artículos) y cree con pipe una función formatPrice que redondee a dos decimales y añada EUR.