Hay una característica casi mística
de funciones de script Java
que muchos desarrolladores no logran comprender por completo.
Y de lo que estoy hablando es de algo llamado cierres.
Así que cuando les pregunté a mis alumnos,
¿Cuál es el concepto de JavaScript más difícil de entender?
entonces mucha gente dice que son cierres.
Sin embargo, creo que con la explicación correcta,
en realidad no es tan dificil
especialmente cuando ya entendiste todo
que aprendiste antes en este curso,
como el contexto de ejecución,
la pila de llamadas y la cadena de escultura,
porque los cierres traen todos estos conceptos
juntos de una manera hermosa, casi mágica.
Así que basta de charla, veamos de qué se tratan los cierres.
Así que voy a empezar creando una nueva función aquí.
llamada reserva segura.
Y es esta función la que creará el cierre.
Ahora, lo primero que necesito decirles sobre los cierres
es que un cierre no es una característica que usamos explícitamente.
Entonces no creamos cierres manualmente,
como si creamos una nueva matriz o una nueva función.
Entonces, un cierre simplemente ocurre automáticamente.
en ciertas situaciones, solo necesitamos
para reconocer esas situaciones.
Y eso es lo que vamos a hacer aquí en este ejemplo.
Así que vamos a crear una de esas situaciones.
para que luego podamos echar un vistazo a un cierre.
De todos modos, ahora continuemos escribiendo este ejemplo.
y estoy llamando a este un pasajeroContar
y empezará de cero, pero estaremos
capaz de manipularlo.
Y estoy llamando a esta función aquí,
reserva segura porque esta variable de número de pasajeros
no puede ser manipulado y accedido desde el exterior.
Entonces, y ahora, ¿qué tiene de especial esta función?
es que devolverá una nueva función.
Y lo que hacemos en esta función es actualizar
la variable número de pasajeros.
Entonces, la variable que se define en la función principal.
Eso es importante.
Y luego registremos el nuevo número de pasajeros
a la consola
Así que pasajeroConde, está bien,
y ahora llamemos a la función de reserva segura
y luego almacenar el resultado en una variable llamada Booker.
Y esto es bastante similar a lo que hicimos
previamente en la lección de funciones
devolviendo otras funciones.
Así que tenemos una función aquí que llamamos
y esta función devolverá esta nueva función.
Y así como llamamos reserva segura,
devolverá exactamente esta función
y luego se almacenará dentro de este Booker.
Y entonces, esto de aquí ahora también será una función, ¿verdad?
Así que analicemos en detalle
qué sucede cuando esta línea de código aquí
se ejecuta usando todos los conceptos
que ya conocemos.
Este es exactamente el código que acabamos de escribir.
Ahora, antes de empezar a correr
la función de reserva segura aquí abajo,
nuestro código se ejecuta en el contexto de ejecución global.
Y allí, actualmente solo tenemos
esta función de reserva segura.
Y entonces también podemos decir que el alcance global
ahora contiene reserva segura.
Luego, cuando se ejecuta la reserva segura,
se pone un nuevo contexto de ejecución
en la parte superior de la pila de ejecución.
Ahora, recuerda, cada contexto de ejecución
tiene un entorno variable,
que contiene todas sus variables locales.
En este caso, solo contiene el número de pasajeros.
puesto a cero.
Este entorno variable también es
el alcance de esta función.
Y así la cadena de alcance de este contexto de ejecución
Se ve como esto.
Así que PassengerCount está en el ámbito local,
pero, por supuesto, este alcance también tiene acceso
a todas las variables de los ámbitos de los padres.
Y en este caso, solo un alcance global.
De todos modos, en la siguiente línea de la función de reserva segura,
se devuelve una nueva función y se almacenará
en la variable Booker.
Entonces, el contexto global ahora también contiene la variable Booker.
Y ahora que mas pasa cuando
vuelve la función de reserva segura?
Bueno, eso es correcto.
Su contexto de ejecución salta de la pila y desaparece.
Así que la función de reserva segura ha hecho su trabajo
y ahora ha terminado la ejecución.
Realmente se ha ido ahora y eso es importante tenerlo en cuenta.
y para tener en cuenta.
Y por ahora, eso es todo lo que hicimos.
Así que todo esto no es nada nuevo a estas alturas, ¿verdad?
Todo lo que hicimos fue analizar la pila de llamadas
y la cadena de alcance como llamamos a la función de reserva segura.
Y esto va a ser importante para más adelante
entender el cierre.
Entonces, hasta el momento, no vimos el cierre todavía.
Todo lo que hicimos fue usar el conocimiento que ya tenemos
para entender cómo se creó esta función de Booker,
porque eso va a ser importante para el próximo paso.
Así que ahora volvamos a nuestro código.
para usar realmente la función Booker,
y finalmente ver el cierre en acción.
Ahora que entendemos cómo funciona Booker
fue creado, llamémoslo ahora realmente aquí.
Así que llamándolo un par de veces aquí, y como podemos ver,
no necesita ningún argumento.
No hay una lista de parámetros, ¿verdad?
Así que llamémoslo aquí tres veces
y ahora vamos a recargar la página aquí.
Y de hecho tenemos uno, dos, tres pasajeros.
Entonces, lo que esto significa es que la función de Booker
de hecho fue capaz de incrementar el número de pasajeros
a uno, luego a dos y luego a tres.
Pero ahora si pensamos en esto,
entonces, ¿cómo es esto posible?
¿Cómo se puede actualizar la función Booker?
esta variable de número de pasajeros que está definida
en una función de reserva segura
que en realidad ya ha terminado de ejecutarse.
Y así, como acabo de decir,
esta función ya ha terminado su ejecución.
Se fue.
Entonces su contexto de ejecución ya no está en la pila,
como acabamos de ver en la diapositiva,
pero aún esta función interna aquí,
que es la función de Booker,
todavía puede acceder a la variable PassengerCount
eso está dentro de la función Booker
que ya no debería existir.
Y tal vez puedas adivinar que lo que hace esto posible
es un cierre, pero antes explico
exactamente cómo funciona el cierre,
Quiero que aprecies una vez más,
qué extraño es esto en realidad.
De nuevo, esta función de Booker aquí
es simplemente una función que existe
aquí en el entorno global
o en el ámbito global, ¿verdad?
Y el entorno en el que se creó la función.
Así que este año básicamente, este ambiente
ya no está activo.
De hecho, se ha ido.
Pero aún así la función de Booker continúa de alguna manera.
tener acceso a las variables
que estaban presentes en el momento en que se creó la función.
Y en particular, esta variable de número de pasajeros aquí.
Y eso es exactamente lo que hace el cierre.
Entonces podemos decir que un cierre hace una función
recordar todas las variables que existieron
esencialmente en el lugar de nacimiento de la función, ¿verdad?
Entonces podemos imaginar la reserva segura
por ser el lugar de nacimiento de esta función.
Así que de la función de Booker, esencialmente.
Y así esta función recuerda todo en su lugar de nacimiento,
en el momento de su creación.
Y esto no se puede explicar simplemente
solo con la cadena de alcance.
Así que también tenemos que entender el cierre.
Y permítanme ahora, realmente explicar cómo funciona realmente.
Así es como dejamos la pila de llamadas
y la cadena de escultura después de la última diapositiva.
Y lo más importante a notar aquí
es que el contexto de ejecución de la reserva segura
ya no está en la pila de llamadas,
porque de nuevo, esta función ha terminado
ejecución hace mucho tiempo.
Así que ahora es el momento de ejecutar finalmente la función Booker.
y ver exactamente lo que va a pasar aquí.
Y tenga en cuenta que Booker es realmente esta función aquí,
ubicado en el ámbito global.
De todos modos, lo primero que va a pasar
es que se crea un nuevo contexto de ejecución
y poner en la parte superior de la pila de llamadas
y el entorno variable de este contexto se vacía
simplemente porque no hay variables
declarado en esta función.
Ahora, ¿qué pasa con la cadena de alcance?
Bueno, dado que Booker está en el contexto global,
es simplemente el alcance de un niño del alcance global,
así, pero tal vez ahora
Estás empezando a ver el problema.
Entonces, ¿cómo accederá la función Booker
la variable conteo de pasajeros?
No se encuentra en ninguna parte de la cadena de alcance, ¿verdad?
Así que aquí es donde empezamos a revelar
el secreto del cierre
y el secreto es básicamente este.
Cualquier función siempre tiene acceso al entorno variable.
del contexto de ejecución en el que se creó la función.
Ahora, en el caso de Booker, se creó esta función.
Nació en el contexto de ejecución de la reserva segura,
que se sacó de la pila anteriormente, ¿recuerdas?
Entonces, por lo tanto, la función de Booker
tendrá acceso a este entorno variable,
que contiene la variable PassengerCount.
Y así es como la función podrá leer
y manipule la variable PassengerCount.
Y entonces es esta conexión la que llamamos cierre.
Así que digamos todo eso de nuevo, para dejar esto realmente claro.
Entonces una función siempre tiene acceso al entorno variable.
del contexto de ejecución en el que fue creado,
incluso después de que se haya ido un contexto de ejecución de deuda.
Y esta última parte es realmente importante.
El cierre es entonces básicamente este entorno variable.
adjunto a la función,
exactamente como era en el momento y lugar
que la función fue creada.
Y esto probablemente todavía suene confuso, pero no te preocupes.
Tengo algunas analogías más familiares en la siguiente diapositiva.
Por ahora, solo estamos tratando de entender el mecanismo.
detrás del cierre, cómo funciona todo detrás de escena.
Entonces, lo que más importa aquí es que la función Booker
tiene acceso a la variable PassengerCount
porque está básicamente definido en el ámbito
en el que se creó realmente la función Booker.
Entonces, en cierto sentido, la cadena de alcance en realidad se conserva
a través del cierre, incluso cuando un alcance
ya ha sido destruido
porque su contexto de ejecución se ha ido.
Esto significa que aunque el contexto de ejecución
en realidad ha sido destruido,
el entorno variable de alguna manera sigue viviendo
en algún lugar del motor.
Ahora podemos decir que la función de Booker cerró
su ámbito principal o sobre su entorno variable principal.
Y esto incluye todos los argumentos de función.
Aunque en este ejemplo, no tenemos ninguno.
Y ahora esta adjunto o cerrado sobre entorno variable
se queda con la función para siempre.
Lo llevará consigo y podrá usarlo para siempre.
Para hacerlo un poco más digerible,
también podemos decir que gracias al cierre,
una función no pierde la conexión con las variables
que existía en el lugar de nacimiento de la función.
Eso es un poco más intuitivo, ¿verdad?
Pero de todos modos, vamos a ver qué pasa ahora
con la ejecución de la función Booker.
Entonces la función intenta aumentar
la variable número de pasajeros.
Sin embargo, esta variable no está en el alcance actual.
Y entonces JavaScript buscará inmediatamente en el cierre.
y ver si puede encontrar la variable allí.
Y lo hace incluso antes de mirar la cadena de alcance.
Por ejemplo, si hubiera una variable global de número de pasajeros
establecido en 10, todavía usaría primero el que está en el cierre.
Entonces, el cierre básicamente tiene prioridad sobre la cadena de alcance.
Y así, después de ejecutar esta función,
el número de pasajeros se convierte en uno.
Este mensaje está registrado.
Y luego el contexto de ejecución se saca de la pila.
Luego, la ejecución pasa a la siguiente línea.
Obtenemos un nuevo contexto de ejecución y un cierre todavía está allí,
todavía adjunto a la función y el valor sigue siendo uno.
Y ahora esta función se ejecuta,
aumentando la cuenta de pasajeros hasta dos
y registrar un mensaje de nuevo.
Bien, y eso es lo que son los cierres.
y cómo trabajan detrás de escena.
Y sé que todo esto es bastante complejo.
Así que déjame darte un par de definiciones diferentes
de cierre ahora, algunos más formales
y algunos más intuitivos y quizás más fáciles de entender.
Y la definición más formal de cierre
es el que ya vimos,
que es que un cierre es la variable cerrada
entorno del contexto de ejecución
en el que se creó una función
incluso después de que ese contexto de ejecución haya desaparecido,
o en otras palabras, incluso después de la función
al que pertenece el contexto de ejecución ha regresado.
A continuación y un poco más fácil de entender,
un cierre le da a una función acceso a todas las variables
de su función madre.
Entonces la función en la que está definida
incluso después de que esa función principal haya regresado.
Entonces la función mantiene una referencia a su ámbito externo.
incluso después de que ese alcance externo se haya ido,
que básicamente preserva la cadena de alcance a lo largo del tiempo.
Muy bien, otra definición,
o digamos analogía es que un cierre se asegura
que una función nunca pierde la conexión
a las variables que existían en el lugar de nacimiento de la función.
Recuerda las variables,
incluso después de que el lugar de nacimiento se ha ido.
Es como una persona que no pierde la conexión.
a su ciudad natal.
En esta analogía, la persona es la función
y la ciudad natal es el alcance de los padres de la función,
y la función entonces no pierde la conexión
a las variables almacenadas en el alcance de este padre.
Y espero que eso tenga sentido.
Finalmente, a algunas personas les gusta pensar en
este adjunto entorno variable como una mochila.
Entonces, en esta analogía, una función tiene una mochila,
que lleva por donde quiera que va.
Y esta mochila contiene todas las variables
que estaban presentes en el ambiente
en el que se creó la función.
Luego, cada vez que no se puede encontrar una variable
en el ámbito de la función,
JavaScript buscará en la mochila.
y toma la variable que falta de allí.
Tan similar a las otras definiciones,
pero tal vez un poco más visual.
Estas son algunas formas diferentes de definir el cierre,
pero todos significan lo mismo.
Así que todos representan la misma idea.
Finalmente, debemos entender que no
tiene que crear cierres manualmente.
Y esto es también lo que ya toqué
al comienzo de la conferencia.
Entonces, en cambio, esto es algo que JavaScript
lo hace de forma totalmente automática, no tenemos que hacer nada.
Además, no hay forma de que podamos acceder explícitamente
cerrado sobre variables.
Eso es porque los cierres no son como algo tangible.
No son como un objeto o para que podamos acceder.
Así que no podemos simplemente llegar a un cierre
y tomar variables de él.
Eso es imposible porque un cierre
es sólo una propiedad interna de una función.
Podemos observar que ocurre un cierre
porque mágicamente las funciones siguen teniendo acceso
a variables que ya no deberían existir,
pero no podemos acceder directamente a estas variables.
Sin embargo, lo que podemos hacer es echar un vistazo
en esta propiedad interna.
Así que en esta mochila, por así decirlo, en una consola.
Así que hagámoslo rápidamente antes de terminar esta lección.
Y podemos hacer esto usando console.dir
y luego de la propia función de Booker.
Tan similar a console.log
pero este es un poco diferente.
Y aquí ahora tenemos esta función en sí.
Entonces podemos obtener los argumentos, la propiedad del nombre
que ya echamos un vistazo antes.
Y luego aquí abajo,
tenemos la propiedad interna de este alcance.
Y la propiedad de este ámbito interno aquí
es básicamente el entorno variable
de la función de Booker.
Ahora aquí, podemos ver el cierre
Viniendo de la reserva segura, ¿de acuerdo?
Y aquí es donde vemos el número de pasajeros,
que actualmente es de tres.
Y entonces este cierre aquí básicamente,
es el entorno variable de esta reserva segura.
Así que ese es el que está siendo preservado por el cierre.
¿Está bien?
Y, por cierto, cada vez que vea estos corchetes dobles aquí,
eso quiere decir que es una propiedad interna,
a la que no podemos acceder desde nuestro código.
Muy bien, este fue un video largo sobre los cierres.
Ahora, en la próxima conferencia,
vamos a echar un vistazo a tres ejemplos más
de cierres y también analizar cómo funcionan,
porque es muy importante que entiendas
este concepto de cierres.
Es una característica que se usa todo el tiempo en JavaScript.
y muchas veces, aun sin que nos demos cuenta
que los cierres están ocurriendo.
Así que si quieres tener confianza como programador,
siempre necesitas saber cómo exactamente
todo en su código funciona.
Y eso, por supuesto, incluye los cierres.