Javascript. ¿Qué es eso de las High-Order-Functions?
Una explicación del concepto de High Order Functions en Javascript y cómo podemos emplearlas

Seguramente si habéis trabajado con React os hayáis topado con el concepto de High Order Component pues se trata de un patrón muy recurrente en esta librería y que permite añadir funcionalidad a los componentes sin modificarlos internamente. De hecho, a partir de él aparecieron librerías muy potentes como recompose que llevan este patrón hasta su máxima expresión.
Esta idea de los HOC’s procede de lo que se conoce como High Order Functions, cuya definición es:
HOF: Una función que acepta y/o devuelve otra función
Por tanto, podríamos decir que el adjetivo de High Order procede de que en vez de operar con strings, números o booleans lo hace con funciones.
Y, concretamente en Javascript, este concepto toma especial relevancia ya que en este lenguaje las funciones son tratadas como si fueran otro tipo más, por lo que podemos:
- asignarlas a variables pudiendo reutilizarlas a lo largo de nuestro código.
- almacenarlas en arrays, cuyo ejemplo más evidente es el funcionamiento del Event Loop de Javascript
- como propiedades de objetos, convirtiéndolas en métodos
- pasarlas como argumentos lo cual nos permite abstraer la funcionalidad y escribir código declarativo gracia a acciones como map, filter o reduce
- y por supuesto devolverlas desde otras funciones.
Es decir, las funciones en Javascript son tratadas como si fueran un first-class citizen al igual que lo son los strings, números, arrays u objetos.
Pasando funciones como argumentos
Una de los ejemplos más claros de cómo podemos pasar funciones vía argumentos es empleando las acciones map, filter o reduce de Javascript.
Por ejemplo:
const duplicate = num => num * 2;
const foo = [1, 2, 3, 4];
const result = foo.map(duplicate); // [2, 4, 6, 8]
Como veis, en este caso estamos pasando al método map
la función flecha duplicate
la cual se encarga de multiplicar por dos el número pasado por argumentos. De este modo, map
itera sobre cada elemento del array foo
y le aplica la función almacenada en la variable duplicate
obteniendo finalmente un array con los elementos multiplicados por dos.
Podéis leer más sobre este tema en otro de los artículos que escribí hace tiempo sobre programación funcional:
Devolviendo funciones
Sin embargo, creo que lo verdaderamente interesante comienza cuando interiorizamos que desde una función podemos devolver otra función:
const addGenerator = (x) => (y) => x + y;
En este caso, cuando ejecutemos add
, por ejemplo, add(3)
lo que obtendremos será una función con este aspecto: (y) => 3 + y
:
const addGenerator = (x) => (y) => x + y;
const addThree = addGenerator(3); // y => 3 + y
const result = addThree(5) // 8
De hecho, los dos últimos pasos podemos comprimirlos en uno solo:
const addGenerator = (x) => (y) => x + y;
const result = addGenerator(3)(5) // 8
Por tanto, el concepto de high order function nos permite dotar a nuestro código de gran reusabilidad y flexibilidad, pues nos permite abstraer la forma en que se hacen determinadas operaciones para modificarlas posteriormente.
Componiendo funciones
Finalmente y aunque no sea esté estrictamente ligado con el concepto de high order function sí que he creído interesante mencionar la posibilidad de componer funciones cuando trabajamos con Javascript.
La composición de funciones es un concepto matemático que se refiere al proceso de combinar dos o más funciones para producir una nueva la cual aplica sucesivamente cada una de las funciones compuestas.
Por ejemplo, si tenemos:
const f = a => a + 2;const g = a => a * 3;
La composición de f
con g
(es decir, g(f(x))
, lo cual se lee al revés como os habréis podido dar cuenta) daría como resultado una función que primero sumaría dos y el resultado lo multiplicaría por 3.
let result = f(5) // 7result = g(result) // 21
Este proceso en Javascript podemos reproducirlo mediante la función pipe
la cual aplica cada función de izquierda a derecha pasando a la siguiente función el resultado de la anterior.
Por ejemplo, escribamos una función que devuelve la propiedad name
de un objeto:
const getName = person => person.name;
getName({ name: 'Gerardo' });
// Gerardo
Ahora escribamos una función que convierte en mayúsculas un string:
const uppercase = foo => foo.toUpperCase();
uppercase('Gerardo');
// GERARDO
Si quisiéramos hacer el proceso completo sobre un objeto que posee una propiedad name
podríamos hacer:
const name = uppercase(getName({ name: 'Gerardo' }));
Como sospechareis, si empezamos a encadenar funciones esta línea puede hacerse excesivamente larga. Es aquí donde entra la función pipe
cuya una de sus posibles implementaciones es la siguiente (la que veréis a continuación está extraída de este artículo de Eric Elliot):
const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
Como veis, pipe
es una high order function que devuelve una función la cual es el resultado de aplicar en orden sucesivo cada función pasada como argumentos (operador rest) gracias a la acción reduce
. Por tanto, ahora podríamos tener la siguiente función:
const getUppercaseName = pipe(getName, uppercase);const result = getUppercaseName({'name': 'Gerardo'}); // GERARDO
Es a partir de aquí donde la cosa se pone interesante si queréis profundizar más en el concepto de programación funcional. Yo os animo hacerlo ya que se trata de un paradigma que está muy en boga actualmente y que supone añadir una nueva forma de pensar, lo cual nunca está de más.
¿Quieres recibir más artículos como este?
Suscríbete a nuestra newsletter: