Hamburger Icon
La memoización en JavaScript

La memoización en JavaScript

La memoización es una técnica usada para que las funciones guarden resultados de sus ejecuciones para optimizar recursos en llamadas posteriores a través de funciones de orden superior.

Antes de ir al meollo del asunto, conviene entender qué son las funciones de orden superior y las closures (cláusulas en castellano, pero me suena raro así que me quedo con closure 😅). Si esto ya te lo sabes, adelante, sáltatelo.

Funciones de orden superior

Una función de orden superior es la que trabaja con otras funciones, ya sea porque las recibe como parámetro o porque devuelve una.

Si alguna vez has usado las funciones map, filter o reduce, entonces ya has usado funciones de orden superior. En estas funciones pasamos una función para que se aplique a cada uno de los elementos del array de una forma concreta en cada caso.

Hacer esto nos sirve para abstraer el comportamiento de la función. Un ejemplo sería este:

https://a.storyblok.com/f/96536/600x576/fb284330e7/funcion-orden-superior.jpg

Podemos ver como en primer lugar, definimos la función de orden superior mayorQue. Esta función devuelve otra función de comparación, que comprueba si el valor pasado a la función de orden superior es inferior al valor que se le pase a la función que devuelve.

Si te has quedado 🤯 vuelve a leerlo más despacio.

Lo siguiente es usar la función de orden superior, de modo que guardamos en la variable mayorQue100 lo que devuelve la función de orden superior cuando le pasamos el valor 100. ¿Y qué devuelve? Pues la función de comparación con 100, que retornará un booleano.

Finalmente, cuando usamos la función mayorQue100 estaremos mirando si el valor que le pasamos es superior o no a 100.

Closures

Las closures son uno de los conceptos clave en JavaScript. Es una función interior con acceso a las variables del entorno de otra función exterior.

Cuando una función se invoca en JavaScript, se crea un nuevo entorno para esa invocación. El entorno se crea para la invocación de la función, no para la función en si misma. Cuando la ejecución de una función termina, el entorno se destruye por el garbage collector.

Ahora bien, si creamos un enlace con el entorno que crea esa invocación, no se destruirá después de la invocación. Ejemplo:

https://a.storyblok.com/f/96536/600x526/e5e272b5c5/closure.jpg

La función saludar es una closure, tiene acceso al entorno de la función exterior saludame. Cuando termina la ejecución de la función saludame, su entorno no es destruido porque la variable saludoParaPol todavía tiene acceso a él, hemos creado un enlace con el entorno, y la única forma posible de acceder al mismo es a través de la closure que devuelve la función.

La closure es la puerta de enlace entre el entorno exterior e interior de la función saludar. Solo podemos acceder a las variables que nos permita la closure, el resto quedan protegidas. ¿Te suena esto a clases y métodos privados?

Las closures nos permiten exponer las partes que nos interese de una función. Otro ejemplo para clarificar un poco más esto último:

https://a.storyblok.com/f/96536/600x1129/2cfebf11bb/closure-2.jpg

En este ejemplo tenemos la función Coche que tratamos como si fuera una clase en el paradigma de orientación a objetos. Exponemos los métodos getMarca, getColor, getKm y dateUnaVuelta, pero nada más, por eso no podemos acceder directamente a la marca ni a nada que no hayamos expuesto.

Memoización

Para conseguir cachear datos en una función vamos a aprovecharnos de las funciones de orden superior y de las closures. Es conveniente usar esta técnica cuando llamamos repetidas veces a una función cuya ejecución puede llegar a ser costosa en términos de recursos.

Con la memoización, conseguimos que al llamar a una función por segunda vez (pasando los mismos parámetros) nos devuelva el mismo resultado que antes sin necesidad de ejecutar su código de nuevo.

Vamos a verlo con un ejemplo simple: una función que suma 5 al valor que sea que le pasemos.

https://a.storyblok.com/f/96536/600x784/fc35b4c7e5/memoizacion.jpg

En este ejemplo la primera vez que llamamos a la función, se calcula el resultado, se guarda en caché y nos lo devuelve. La segunda vez que la llamamos pasando el mismo parámetro, encuentra el valor en caché y lo devuelve directamente.

Aunque el ejemplo propuesto no sea "real", imagina la situación en que un cálculo determinado tarde 500ms aproximadamente y tengas que llamar a la función en un bucle más de 100 iteraciones. La memoización puede resultar muy útil, ¿no crees?