Closure es un concepto confuso por que su uso es “invisible”.

A diferencia de otros conceptos como funciones, variables u otros. Los closures no son siempre utilizados a conciencia y de forma directa. No dices: Oh aquí usaré un closure como solución.

Pero al mismo tiempo, lo más probable es que ya lo has usado cientos de veces. Aprender closure es más sobre identificar cuando lo estas utilizando que aprender un nuevo concepto en sí.

TLDR;

Tienes una closure cuando una función cualquiera accede a una variable fuera de su contexto.

const valor = 1
function obtenerModulo() {
    let datos = [1,2,3,4,5,6,7,8,9,10,11]
    return datos.filter(item => item % valor === 0)
}

Aqui la función `obtenerModulo` hace uso de `valor`. Pero también la función `item => item % valor === 0` que también puede ser escrita como

function(item){
    return item % valor === 0
}

utiliza el valor de la variable `value` que fue definida fuera de la función.

Las funciones pueden acceder valores fuera de su contexto

Como en el ejemplo anterior, una función puede acceder y utilizar valores que están definidos fuera de su “cuerpo” o contexto, por ejemplo

let count = 1
function contador() {
    console.log(count)
}
contador() // imprime 1
count = 2
contador() // imprime 2

Esto, permite que podamos modificar el valor de la variable `count` desde cualquier parte del módulo y, cuando la función contador sea llamada, sabrá usar el valor actual.

¿Por qué usamos funciones?

Pero, por que utilizamos funciones en nuestros programas?, Ciertamente es posible - dificil, pero posible - escribir un programa sin utilizar funciones definidas por nosotros mismos, entonces ¿por qué creamos funciones propias?

Imagina un trozo de código que hace algo maravilloso , lo que sea, y está compuesto por X número de lineas

/* Mi trozo de codigo maravilloso */

Ahora, supón que debes utilizar este trozo de codigo maravilloso en varias partes de tu programa, ¿Qué harías?.
La opción “natural” es juntar este trozo de código en un conjunto que pueda ser reutilizable, y ese conjunto reutilizable es lo que llamamos función. Las funciones son la mejor forma de reutilizar y compartir código dentro de un programa.

Ahora, puedes utilizar tu función cuantas veces sean y - ignorando algunos casos particulares- llamar tu función N veces es lo mismo que escribir ese trozo de código maravilloso N veces. Es un simple reemplazo.

¿Pero dónde está el closure aquí?

Usando el ejemplo del contador, consideremos ese como el trozo de código maravilloso

let count = 1
function contador() {
    console.log(count)
}
contador() // imprime 1

Ahora, queremos reutilizarlo en muchas partes, por lo que lo "envolveremos" en una función.

function miFuncion() {
    let count = 1
    function contador() {
        console.log(count)
    }
    contador() // imprime 1
}

ahora, que tenemos?

Una función: `contador` que utiliza un valor que fue declarado fuera de ella `count`. Y un valor: `count` que fue declarado en la función `miFuncion` pero que es usado dentro de la función `contador`.

Es decir, tenemos una función que utiliza un valor que fue declarado fuera de su contexto: un closure.

¿Simple no? Ahora, ¿qué pasa cuándo se ejecuta la función `miFuncion`?, ¿qué ocurre con la variable `count` y la función `contador`?
Una vez ejecutada la función padre, las variables y funciones declaradas en su cuerpo “desaparecen” (garbage collector).

Ahora, modifiquemos un poco el ejemplo:

function miFuncion() {
    let count = 1
    function contador() {
        count++
        console.log(count)
    }
   setInterval(contador, 2000)
}
miFuncion()

¿Qué ocurrirá ahora con la variable y función declaradas dentro de `marvel`?
En este ejemplo, le decimos al browser que ejecute `contador` cada 2 segundos. Por lo que el engine de javascript debe mantener una referencia a la función y tambien a la variable que es utilizada por ella. Entonces, incluso una vez que la función padre `miFuncion` termine su ciclo de ejecución, la función `contador` y el valor `count` seguiran “viviendo”.

Este “efecto” de tener closures ocurre por qué javascript soporta la anidación de funciones, o en otras palabras: Las funciones son ciudadanos de primera clase en el lenguaje y pueden ser utilizadas como cualquier otro objeto: anidadas, pasadas como argumento, como valor de retorno, etc.

¿Qué puedo hacer con closures?

Expresión de Función Inmediatamente invocada - Immediately-invoked Function Expression (IIFE)

Es una ténica utilizada mucho en los días de ES5 para implementar el patrón de diseño de “modulo” (antes de que esto fuese soportado nativamente).
La idea es “envolver” tu módulo en una función que es inmediatamente ejecutada

(function(arg1, arg2){
...
...
})(arg1, arg2)

Esto permitía el uso de variables privadas que sólo podían ser utilizadas por el propio módulo dentro de la función, es decir, permitía emular los modificadores de acceso.

const modulo = (function(){
	function metodoPrivado () {
	}
	const valorPrivado = "algo"
	return {
	  get: valorPrivado,
	  set: function(v) { valorPrivador = v }
	}
})()

var x = modulo()
x.get() // "algo"
x.set("Otro valor")
x.get() // "otro valor"
x.valorPrivado //Error

Function Factory

Otro patrón de diseño implementado gracias a los closures es la “Fábrica de Funciones” (Function Factory),  funciones que crean funciones u objetos, por ejemplo, una función que te permite crear objetos usuarios.


const crearUsuario = ({ userName, avatar }) => ({
      id: crearUnID(),
      userName,
      avatar,
      cambiarUserName (userName) {
        this.userName = userName;
        return this;
      },
      cambiarAvatar (url) {
        // ejecuta logica para obtener el avatar desde la url
        const nuevoAvatar = obtenerAvtarDesdeUrl(url)
        this.avatar = nuevoAvatar
        return this
      }
    });
    
        console.log(crearUsuario({ userName: 'Bender', avatar: 'bender.png' }));
    
    {
      "id":"17hakg9a7jas",
      "avatar": "bender.png",
      "userName": "Bender",
      "cambiarUsername": [Function cambiarUsername]
      "cambiarAvatar": [Function cambiarAvatar]
    
    }
    */

Y utilizando este patrón, es posible implementar una idea proviniente de la programación funcional: Currying

Currying

Es un patrón de diseño ( y característica de algunos lenguajes ) en donde una función, es inmediatamente evaluada y retorna una segunda función.
Este patrón permite ejecutar especialización y composición.

Estas funciones “curried” son creadas utilizando closures, definiendo y retornando la función interna del closure.

function multiplicar(a) {

    return function (b) {
        return function (c)  {
            return a * b * c
        }
    }
}
let mc1 = multiplicar(1);
let mc2 = mc1(2);
let res = mc2(3);
console.log(res);

let res2 = multiplicar(1)(2)(3);
console.log(res2);

Este tipo de funciones toman un solo valor o argumento y retornan otra función que también recibe un argumento. Es una aplicación parcial de los argumentos. También es posible reesscribir este ejemplo utilizando ES6.

let multiplicar = (a) => (b) => (c) => {

    return a * b * c;
}

let mc1 = multiplicar(1);
let mc2 = mc1(2);
let res = mc2(3);
console.log(res);

let res2 = multiplicar(1)(2)(3);
console.log(res2);

¿Dónde podemos aplicar el uso de currying?, en composición, digamos que tienes una función que crea elementos html.

function crearElemento(elemento){
    const el = document.createElement(elemento)
    return function(contenido) {
        return el.textNode = contenido
    }
}

const negrita = crearElemento('b')
const italica = createElemento('i')
const contenido = 'Mi contenido'
const miElemento  = negrita(italica(contenido)) // <b><i>Mi contenido</i></b>

Event Listeners

Otro lugar en donde puedes utilizar y aplicar closures es en los manejadores de eventos utilizando React.

Supongamos que estas utilizando una librería de terceros para renderizar los items de tu colección de datos, esta libraría expone un componente llamado `RenderItem` que tiene sólo una prop disponible `onClick`. Esta prop no recibe parámetro alguno y tampoco retorna un valor.

Ahora, en tu particular app, requieres que al hacer click en el item se muestre una alerta con el título del item, pero el evento onClick que tienes dispobile no acepta argumentos, que puedes hacer?
Closures al rescate:

// Esta es el closure
// en es5
function onItemClick(titulo) {
    return function() {
      alert("Click en " + titulo)
    }
}
// en es6
const onItemClick = titulo => () => alert(`Click en ${titulo}`)

return (
  <Contenedor>
{items.map(item => {
return (
   <RenderItem onClick={onItemClick(item.titulo)}>
    <Titulo>{item.titulo}</Titulo>
  </RenderItem>
)
})}
</Contenedor>
)

En este simplificado ejemplo lo que hacemos es crear una función que recibe el titulo que quieres mostrar y retorna otra función que cumple con la definición de la función que RenderItem recibe como prop.

Conclusión

Si bien puedes desarrollar una app sin siquiera saber que estás utilizando closures, el conocer su existencia, definición y uso desbloquea nuevas posibilidades a la hora de crear una solución. Closures es uno de esos conceptos que se complican en entender cuando estás empezando, pero hacer el intento de utilizarlos con conocimiento puede permitirte aumentar tus herramientas y avanzar en tu carrera.

Footer-Social-Card

? Sígueme en Twitter           ✉️ Únete al newsletter                    ❤️ Apoya mi trabajo