Cómo funcionan los cierres de JavaScript, en inglés sencillo
JavaScript es un lenguaje ampliamente adoptado que puedes utilizar para construir cualquier cosa, desde una simple página de aterrizaje hasta una aplicación de producción completa. A medida que JavaScript y la programación en general evolucionaron, los desarrolladores se dieron cuenta de que el paradigma de la programación orientada a objetos (POO) es indeseable para la mayoría de los casos de uso. La programación funcional surgió como la solución a muchos de los puntos de dolor asociados con la POO.
Los cierres son un tema ampliamente discutido en el mundo de la programación funcional, pero a menudo se definen de forma imprecisa y en la jerga técnica. Haremos todo lo posible aquí para explicar cómo funcionan los cierres de JavaScript en términos de lenguaje sencillo.
Al final de este tutorial, deberías entender:
- Cómo identificar los cierres
- Qué es un cierre y cómo se comporta en relación con el contexto de ejecución y la pila de llamadas
- Casos de uso comunes para los cierres
Entendiendo los cierres de JavaScript
Empezaremos mostrando cómo es un cierre.
Intenta ejecutar el código por tu cuenta. Técnicamente, la función llamada makeCounter
devuelve otra función llamada increment
. Esta función increment
tiene acceso a la variable count
incluso después de que se haya ejecutado la función makeCount
. Parte del cierre aquí es la variable count
; está disponible para la función increment
cuando se define, incluso después de que makeCounter
termine. La otra parte es la función increment
.
Imagina que tienes una casa y un jardín que la rodea. Una vez que abres la puerta del jardín y la cierras, no puedes volver a abrirla: el jardín queda inaccesible. Tienes hambre y, afortunadamente, hay un naranjo y un manzano en tu jardín. Coges una pequeña bolsa, arrancas una naranja y una manzana y vuelves a entrar en tu casa. Recuerda que no puedes volver a salir.
Ahora, una vez que estás dentro de tu casa, puedes sacar la naranja o la manzana de la bolsa y comerla cuando vuelvas a tener hambre. La bolsa pequeña en este ejemplo es el cierre. Un cierre contiene todas las variables y funciones que estaban a tu disposición cuando estabas en el jardín, incluso cuando estás dentro de la casa y no puedes volver a salir.
Veamos cómo se desarrolla esto en el código:
Como la variable fruits
está disponible para la función devuelta cuando se ejecuta makeFruitGarden
, la variable fruits
y la función interna se convierten en el cierre. Cada vez que se ejecuta consumeFruit
, se devuelve un fruit
– el último elemento del array fruits
porque se está utilizando pop()
. Una vez que ambas frutas han sido consumidas/comidas, no quedará nada que comer.
Entendiendo el ámbito léxico
Para entender realmente los cierres, debes estar familiarizado con el término «ámbito». El ámbito léxico es un término elegante para el entorno actual relativo a lo que sea que se refiera.
En el siguiente ejemplo, el ámbito de la variable llamada myName
se llama «ámbito global».
// global scopeconst myName = "John Doe"function displayName() { // local/function scope console.log(myName);};displayName()
Puede que hayas visto este concepto referenciado al leer cómo var
no tiene ámbito de bloque y cómo const
let
sí. Es importante tener en cuenta que en JavaScript, una función siempre crea su propio ámbito. Esto se llama el ámbito local
o function
, como se muestra en el ejemplo de código.
Si has estado prestando atención, podrías estar pensando que myName
y displayName
son parte de un cierre. Estarías en lo cierto. Pero como la función y la variable aquí existen en el ámbito global, no tiene mucho valor llamarlo cierre.
Hay muchos tipos de ámbitos en JavaScript, pero cuando se trata de cierres, hay tres ámbitos que debes conocer:
- El ámbito global es el ámbito por defecto donde viven todos. Piensa en él como tu calle
- El ámbito externo de la función es la función que devuelve una función. Tiene su propio ámbito. Piensa en él como tu jardín
- El ámbito de la función interna/local es la función devuelta que se convierte en un cierre. Piensa en ello como tu casa
Ahora vamos a sumergirnos en algunos casos de uso.
Casos de uso comunes para cierres
Currying
El currying de funciones es otro concepto poderoso en la programación funcional. Para implementar una función curry en JavaScript, se utilizarían closures.
Currying una función puede describirse como la transformación de una función y se ejecuta así: add(1, 2, 3)
a add(1)(2)(3)
.
function add(a) { return function(b) { return function(c) { return a + b + c; }; };};add(1)(2)(3) // returns 6
La función add
toma un solo argumento y luego devuelve dos funciones que se anidan una tras otra. El objetivo del currying es tomar un montón de argumentos y finalmente terminar con un único valor.
Funciones de orden superior
El objetivo de una función de orden superior es tomar una función como argumento y devolver un resultado. Los métodos de array como map
y reduce
son ejemplos de funciones de orden superior.
const arrayOfNumbers = ;const displayNumber = (num) => { console.log(num);}arrayOfNumbers.forEach(displayNumber)
La función de orden superior Array.prototype.forEach
aquí acepta displayNumber
como argumento y luego la ejecuta para cada elemento en el arrayOfNumbers
. Si has utilizado un framework de interfaz de usuario como Vue o React, puede que estés familiarizado con los componentes de orden superior, que son esencialmente lo mismo que las funciones de orden superior.
Entonces, ¿cuál es la diferencia entre las funciones de orden superior y el currying? Mientras que una función de orden superior toma una función como argumento y devuelve un valor, una función de curry devuelve una función como resultado, lo que finalmente lleva a un valor.
Administradores de elementos DOM
Este es un patrón de diseño común que se utiliza a menudo para obtener y establecer propiedades de elementos DOM. En el siguiente ejemplo, haremos un gestor de elementos para estilizar elementos.
makeStyleManager
devuelve un objeto que da acceso a dos funciones, que forman parte de un cierre junto a las variables element
y currentStyles
. Incluso después de que makeStyleManager
haya terminado de ejecutarse, las funciones getStyle
y setStyle
tienen acceso a las variables.
Conclusión
Los cierres de JavaScript pueden ser difíciles de entender, incluso para desarrolladores con experiencia profesional a sus espaldas. Entender los cierres acabará por convertirte en un mejor desarrollador.
Ahora deberías ser capaz de identificar un cierre cuando se está utilizando en una base de código que parece extraña o no tiene sentido. Los cierres son un concepto crítico en la programación funcional y espero que esta guía te haya ayudado a dar un paso adelante en tu viaje hacia su dominio.
LogRocket: Depura los errores de JavaScript más fácilmente entendiendo el contexto
Depurar código es siempre una tarea tediosa. Pero cuanto más entiendas tus errores más fácil será solucionarlos.
LogRocket te permite entender estos errores de formas nuevas y únicas. Nuestra solución de monitorización del frontend rastrea el compromiso del usuario con sus frontends de JavaScript para darle la capacidad de averiguar exactamente lo que el usuario hizo que condujo a un error.
LogRocket registra los registros de la consola, los tiempos de carga de la página, los stacktraces, las solicitudes/respuestas de red lentas con cabeceras + cuerpos, los metadatos del navegador y los registros personalizados. Entender el impacto de su código JavaScript nunca será más fácil!
Pruébalo gratis.
Más lecturas
- «Nunca entendí los cierres de JavaScript» – Una guía técnica y en profundidad para entender cómo funciona un cierre. Ideal para alguien que quiera entender los cierres con respecto a la
ExecutionContext
, la pila de llamadas, el entorno léxico, etc. - Referencia MDN para los cierres de JavaScript – Una guía de referencia rápida para los cierres (incluyendo algunos errores comunes) en caso de que necesites repasar tu memoria rápidamente
- «Domina la entrevista de JavaScript: ¿Qué es un cierre?» – Los cierres explicados por Eric Elliot; ideal para los desarrolladores que tienen una comprensión de los conceptos de programación funcional