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.