Los cierres en JavaScript son uno de esos conceptos que a muchos les cuesta entender. En el siguiente artículo, voy a explicar en términos claros lo que es un cierre, y voy a llevar el punto a casa usando ejemplos de código simples.
Un cierre es una característica en JavaScript donde una función interna tiene acceso a las variables de la función externa (que encierra) – una cadena de alcance.
El cierre tiene tres cadenas de ámbito:
- tiene acceso a su propio ámbito – las variables definidas entre sus llaves
- tiene acceso a las variables de la función externa
- tiene acceso a las variables globales
Para los no iniciados, esta definición puede parecer un montón de jerga
Pero ¿qué es realmente un cierre?
Veamos un sencillo ejemplo de cierre en JavaScript:
function outer() { var b = 10;
function inner() {
var a = 20;
console.log(a+b);
}
return inner;
}
Aquí tenemos dos funciones:
- una función externa
outer
que tiene una variableb
, y devuelve la funcióninner
- una función interna
inner
que tiene su variable llamadaa
, y accede a una variableouter
b
, dentro de su cuerpo de función
El ámbito de la variable b
se limita a la función outer
, y el alcance de la variable a
se limita a la función inner
.
Invoquemos ahora la función outer()
, y almacenemos el resultado de la función outer()
en una variable X
. Invocamos entonces la función outer()
una segunda vez y la almacenamos en la variable Y
.
Veamos paso a paso lo que ocurre cuando se invoca por primera vez la función outer()
:
- Se crea la variable
b
, se limita su ámbito a la funciónouter()
y se establece su valor a10
. - La siguiente línea es una declaración de función, por lo que no hay nada que ejecutar.
- En la última línea,
return inner
busca una variable llamadainner
, encuentra que esta variableinner
es en realidad una función, por lo que devuelve el cuerpo completo de la funcióninner
. - El contenido devuelto por la sentencia return se almacena en
X
.
Así,X
almacenará lo siguiente:function inner() {
var a=20;
console.log(a+b);
} - La función
outer()
termina de ejecutarse, y todas las variables dentro del ámbito deouter()
ya no existen.
Esta última parte es importante de entender. Una vez que una función completa su ejecución, cualquier variable que fue definida dentro del ámbito de la función deja de existir.
El tiempo de vida de una variable definida dentro de una función es el tiempo de vida de la ejecución de la función.
Lo que esto significa es que en console.log(a+b)
, la variable b
sólo existe durante la ejecución de la función outer()
. Una vez que la función outer
ha finalizado su ejecución, la variable b
deja de existir.
Cuando la función se ejecuta por segunda vez, las variables de la función se crean de nuevo, y viven sólo hasta que la función finaliza su ejecución.
Así, cuando outer()
se invoca la segunda vez:
- Se crea una nueva variable
b
, su ámbito se limita a la funciónouter()
, y su valor se establece en10
. - La siguiente línea es una declaración de función, por lo que no hay nada que ejecutar.
-
return inner
devuelve todo el cuerpo de la funcióninner
. - El contenido devuelto por la declaración de retorno se almacena en
Y
. - La función
outer()
finaliza su ejecución, y todas las variables dentro del ámbito deouter()
dejan de existir.
El punto importante aquí es que cuando la función outer()
se invoca por segunda vez, la variable b
se crea de nuevo. Además, cuando la función outer()
termina de ejecutarse por segunda vez, esta nueva variable b
vuelve a dejar de existir.
Este es el punto más importante a tener en cuenta. Las variables dentro de las funciones sólo nacen cuando la función se está ejecutando, y dejan de existir una vez que las funciones completan su ejecución.
Ahora, volvamos a nuestro ejemplo de código y veamos X
y Y
. Como la función outer()
al ejecutarse devuelve una función, las variables X
y Y
son funciones.
Esto se puede comprobar fácilmente añadiendo lo siguiente al código JavaScript:
console.log(typeof(X)); //X is of type function
console.log(typeof(Y)); //Y is of type function
Como las variables X
y Y
son funciones, podemos ejecutarlas. En JavaScript, una función se puede ejecutar añadiendo ()
después del nombre de la función, como X()
y Y()
.
Cuando ejecutamos X()
y Y()
, estamos ejecutando esencialmente la función inner
.
Examinemos paso a paso lo que ocurre cuando X()
se ejecuta la primera vez:
- Se crea la variable
a
, y su valor se establece en20
. - JavaScript intenta ahora ejecutar
a + b
. Aquí es donde las cosas se ponen interesantes. JavaScript sabe quea
existe ya que lo acaba de crear. Sin embargo, la variableb
ya no existe. Comob
forma parte de la función externa,b
sólo existiría mientras la funciónouter()
está en ejecución. Como la funciónouter()
terminó de ejecutarse mucho antes de que invocáramosX()
, cualquier variable dentro del ámbito de la funciónouter
deja de existir, y por tanto la variableb
deja de existir.
¿Cómo maneja esto JavaScript?
Cierres
La función inner
puede acceder a las variables de la función que la encierra debido a los cierres en JavaScript. En otras palabras, la función inner
conserva la cadena de alcance de la función adjunta en el momento en que se ejecutó la función adjunta, y por lo tanto puede acceder a las variables de la función adjunta.
En nuestro ejemplo, la función inner
había conservado el valor de b=10
cuando se ejecutó la función outer()
, y siguió conservándolo (cerrándolo).
Ahora consulta su cadena de alcance y se da cuenta de que sí tiene el valor de la variable b
dentro de su cadena de alcance, ya que había encerrado el valor de b
dentro de un cierre en el momento en que se había ejecutado la función outer
.
Así, JavaScript conoce a=20
y b=10
, y puede calcular a+b
.
Puedes comprobarlo añadiendo la siguiente línea de código al ejemplo anterior:
Abre el elemento Inspect en Google Chrome y ve a la Consola. Puedes expandir el elemento para ver realmente el elemento Closure
(mostrado en la antepenúltima línea de abajo). Observa que el valor de b=10
se conserva en el Closure
incluso después de que la función outer()
termine su ejecución.

Volvamos ahora a revisar la definición de cierres que vimos al principio y veamos si ahora tiene más sentido.
Pues la función interior tiene tres cadenas de alcance:
- acceso a su propio ámbito – variable
a
- acceso a las variables de la función
outer
– variableb
, que encerraba - acceso a cualquier variable global que pueda estar definida
Cierres en acción
Para hacer entender el punto de los cierres, vamos a aumentar el ejemplo añadiendo tres líneas de código:
Cuando ejecutes este código, verás la siguiente salida en el console.log
:
a=20 b=10
a=20 b=11
a=20 b=12
a=20 b=10
¡Examinemos este código paso a paso para ver qué ocurre exactamente y ver los cierres en Acción!
var X = outer(); // outer() invoked the first time
La función outer()
se invoca la primera vez. Se producen los siguientes pasos:
- Se crea la variable
b
y se establece como10
Se crea la variablec
, y se establece en100
Llamemos a estob(first_time)
yc(first_time)
para nuestra propia referencia. - La función
inner
es devuelta y asignada aX
En este punto, la variableb
está encerrada dentro de la cadena de alcance de la funcióninner
como un cierre conb=10
, ya queinner
utiliza la variableb
. - La función
outer
finaliza su ejecución, y todas sus variables dejan de existir. La variablec
deja de existir, aunque la variableb
existe como cierre dentro deinner
.
var Y= outer(); // outer() invoked the second time
- La variable
b
se crea de nuevo y se establece en10
La variablec
se crea de nuevo.
Nota que aunqueouter()
se ejecutó una vez antes de que las variablesb
yc
dejaran de existir, una vez que la función terminó de ejecutarse se crean como variables completamente nuevas.
Llamemos a estasb(second_time)
yc(second_time)
para nuestra propia referencia. - La función
inner
es devuelta y asignada aY
En este punto la variableb
está encerrada dentro de la cadena de alcance de la funcióninner
como un cierre conb(second_time)=10
, ya queinner
utiliza la variableb
. - La función
outer
finaliza su ejecución, y todas sus variables dejan de existir.
La variablec(second_time)
deja de existir, aunque la variableb(second_time)
existe como cierre dentro deinner
.
Ahora veamos qué ocurre cuando se ejecutan las siguientes líneas de código:
X(); // X() invoked the first time
X(); // X() invoked the second time
X(); // X() invoked the third timeY(); // Y() invoked the first time
Cuando se invoca X()
por primera vez,
- se crea la variable
a
, y se establece en20
- el valor de
a=20
, el valor deb
es del valor de cierre.b(first_time)
, por lo queb=10
- las variables
a
yb
se incrementan en1
. -
X()
completa la ejecución y todas sus variables internas – la variablea
– dejan de existir.
Sin embargo,b(first_time)
se conservó como cierre, por lo queb(first_time)
sigue existiendo.
Cuando se invoca X()
por segunda vez,
- la variable
a
se crea de nuevo, y se establece como20
Cualquier valor anterior de la variablea
ya no existe, ya que dejó de existir cuandoX()
terminó de ejecutarse la primera vez. - El valor de
a=20
el valor deb
se toma del valor de cierre –b(first_time)
También hay que tener en cuenta que habíamos incrementado el valor deb
en1
desde la ejecución anterior, por lo queb=11
- las variables
a
yb
se incrementan en1
de nuevo -
X()
completa la ejecución y todas sus variables internas -la variable a- dejan de existir
Sin embargo,b(first_time)
se conserva ya que el cierre sigue existiendo.
Cuando se invoca X()
por tercera vez,
- la variable
a
se crea de nuevo, y se establece como20
Cualquier valor anterior de la variablea
ya no existe, ya que dejó de existir cuandoX()
terminó de ejecutarse la primera vez. - El valor de
a=20
, el valor deb
es del valor de cierre –b(first_time)
También hay que tener en cuenta que habíamos incrementado el valor deb
en1
en la ejecución anterior, por lo queb=12
-
X()
completa la ejecución, y todas sus variables internas – la variablea
– dejan de existir
Sin embargo,b(first_time)
se conserva ya que el cierre sigue existiendo - la variable
a
se crea de nuevo, y se establece en20
- el valor de
a=20
, el valor deb
es del valor de cierre –b(second_time)
, por lo queb=10
- las variables
a
yb
se incrementan en1
-
Y()
completa la ejecución, y todas sus variables internas – la variablea
– dejan de existir
Sin embargo,b(second_time)
se conservó como cierre, por lo queb(second_time)
sigue existiendo.
Las variables a
y b
se incrementan en 1
de nuevo
Cuando se invoca Y() por primera vez,
Observaciones finales
Los cierres son uno de esos conceptos sutiles en JavaScript que son difíciles de entender al principio. Pero una vez que los entiendes, te das cuenta de que las cosas no podían ser de otra manera.
¡Espero que estas explicaciones paso a paso te hayan ayudado a entender realmente el concepto de cierres en JavaScript!
Otros artículos:
- Una guía rápida de funciones «autoinvocables» en Javascript
- Entendiendo el ámbito de la función vs. el ámbito del bloque en Javascript
- Cómo usar promesas en JavaScript
- Cómo construir una sencilla animación Sprite en JavaScript
.