Qué es el concepto de hoisting que tenemos en Javascript y cómo funciona el mecanismo de doble pasada del intérprete con respecto a las variables.
Javascript es un lenguaje bastante particular y tiene algunos mecanismos que no existen en otros lenguajes de programación, como es el caso del hoisting. Debemos entender cómo funciona el intérprete de Javascript y su doble pasada para entender el hoisting y, en definitiva, para poder dominar este apasionante lenguaje.
En este artículo vamos a resumir algunos mecanismos interesantes del intérprete de Javascript que nos servirán para ser un poco más conscientes de algunos funcionamientos del lenguaje.
Intérprete de Javascript
Javascript es un lenguaje interpretado. Esto quiere decir que no se compila como otros lenguajes del estilo de C o Java, sino que existe un proceso mediante el cual el código se interpreta cada vez que un módulo debe de ejecutarse.
El intérprete simplemente lee el código, realiza los procesos que tiene que hacer para entenderlo y luego se encarga de ejecutar el código, realizando aquellas cosas que se solicitan en el script.
Doble pasada del intérprete
El intérprete trabaja mediante un proceso de doble pasada, es decir, realiza dos recorridos al código para poder ejecutar y realizar el trabajo solicitado en las líneas del programa. Durante estas dos pasadas realiza las siguientes operaciones.
1.- En la primera pasada lee todas las variables y funciones y reserva espacio en memoria para ellas.
2.- En la segunda pasada se encarga de realizar la ejecución del código.
En la primera pasada, a pesar de que lea todas las variables, no les asigna valores. Solo es en la segunda pasada en la que, al ejecutarse el código, las variables toman valores.
Ejemplo de ejecución de un código Javascript y la doble pasada del intérprete
Ahora veamos de manera más práctica qué es lo que pasa cuando se produce esa doble pasada con las variables declaradas en Javascript.
alert(ocupacion);
var ocupacion = 'desarrollador';
alert(ocupacion);
Este código tiene una particularidad importante. Se utiliza una variable antes de su declaración. En la mayoría de los lenguajes esto provocaría un error, dado que no puedes generalmente hacer uso de una variable que no ha sido creada. Sin embargo, el intérprete de doble pasada de Javascript permite que se ejecute sin problemas.
En la primera pasada el intérprete de Javascript reserva un espacio de memoria para la variable "ocupacion''. Sin embargo, esa variable queda indefinida (valor undefined de Javascript).
En la segunda pasada comienza con una variable "ocupacion" que se encuentra con el valor "undefined", y entonces se encarga de ejecutar el código.
Al ejecutarse la primera línea de código el alert muestra el valor actual de la variable "ocupacion", por lo tanto mostrará "undefined". Esa variable ya existe por la primera pasada! por eso en lugar de mostrar un error, saca el valor indefinido.
En la segunda línea de código se asigna un valor a la variable, por lo que al realizarse la ejecución de la tercera línea de código se muestra el valor asignado a la variable: "desarrollador"
Funciones y su contexto de ejecución
Ahora tenemos que analizar qué pasa con las funciones y cuando se produce la doble pasada del intérprete, porque ellas también generan un espacio de variables y por tanto** se realiza también una doble pasada**, la primera para la creación de los espacios de memoria y luego la segunda para su ejecución.
En la primera pasada del contexto actual, cada vez que el intérprete de Javascript ve una función, produce un espacio en memoria donde guarda tal función. Reserva simplemente ese espacio en memoria, donde queda almacenada la función, sin interpretarla todavía y por supuesto sin ejecutarla.
A la vista de ese procedimiento, un código como este no resulta problemático para Javascript, a pesar que estemos invocando la función antes de definirla.
saluda();
function saluda() {
console.log('Hola ' + nombre);
var nombre = 'Miguel';
}
Esto es porque en la primera pasada la función se almacena en la memoria. Por lo que, antes de comenzar la ejecución del código, la función ya realmente existe.
Doble pasada al ejecutarse una función
La doble pasada también se produce en el tiempo de ejecución de una función. Por lo tanto, al invocarse la función se crea un nuevo contexto de ejecución, que tiene el mismo proceso de interpretación que el contexto global.
- En la primera pasada del contexto de ejecución de la función se crean las variables en la memoria, aunque con el el valor "undefined".
- En la segunda pasada del contexto de ejecución de la función se ejecuta el código, igual que ocurría con el contexto global.
Pila de ejecución de las funciones
Este mismo proceso del intérprete de Javascript ocurre cada vez que una función se invoca. Se crea un nuevo contexto de ejecución para cada invocación, siendo que esos contextos pueden estar unos dentro de otros, en el caso de que unas funciones invoquen a otras.
Los contextos por tanto se van introduciendo de manera que ocupan una pila de ejecución, produciéndose esa doble pasada del intérprete para cada uno de ellos. A medida que las ejecuciones de las funciones finalizan la pila se va vaciando, hasta llegar de nuevo al contexto global.
Hoisting
Existe un término en Javascript llamado "hoisting", que hace referencia justamente a este procedimiento por el cual las variables son creadas antes de su uso.
Dado el concepto de doble pasada que acabamos de aprender, toda variable creada en un script Javascript es tenida en cuenta, reservando espacio para ella en memoria, antes de su utilización durante el tiempo de ejecución y esto ocurre tanto en el contexto global como en el contexto de ejecución de una función.
Veamos la función siguiente:
function hoisting() {
console.log(lenguaje);
var lenguaje = "Javascript";
console.log(lenguaje);
}
En esta función se define una variable lenguaje, a la que se asigna el valor después de usarla. Dada la regla de hoisting este código sería equivalente a crear la variable en la primera línea de código de la función, tal como aparece aquí.
function hoisting() {
var lenguaje;
console.log(lenguaje);
lenguaje = "Javascript";
console.log(lenguaje);
}
Este segundo código mostraría cómo realmente Javascript realiza las cosas cuando se invoque la función. Como puedes ver, primero crea las variables en memoria y luego ejecuta el código. Por tanto, ese es el motivo por el cual en el primer console.log() la variable "lenguaje" vale undefined y en el segundo console.log() la variable ya tiene el valor "Javascript".
Buenas prácticas en la declaración de variables Javascript
Finalmente, y como conclusión, dado el mecanismo de hoisting de Javascript que acabamos de explicar, es una buena práctica crear las variables antes de usarlas, en las primeras líneas de código del contexto global, o bien al comienzo de las funciones.
No es que Javascript lo necesite, pero sí ayuda a que las personas entiendan qué es lo que va a pasar realmente a la hora de ejecutarse el código, evitando posibles sorpresas. Tú, que ya sabes lo que es el hoisting y cómo funciona, no vas a tener dudas sobre qué ocurre en tu script, pero otras personas que lean el código lo agradecerán.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...