braindump

Cómo funciona JavaScript: Hoisting

En el post anterior sobre expresiones y declaraciones de funciones comente un poco sobre Hoisting y como cambia la manera en la que nuestro código se ejecuta. Veamos en más detalle qué es realmente lo que el intérprete de JavaScript hace para ejecutar nuestro código y asi ententer alguno que otro error habitual.

¿Qué es Hoisting?

Hoisting significa elevación. Lo que quiere decir es que ciertas partes de nuestro código se elevarán y se ejecutarán antes. veamos el siguiente código:

function obtenerMisArticulos(lista) {
  var articulos = [];
  for (var i = 0; i < lista.length; i++) {
    var elemento = lista[i];
    if (elemento.autor == "Horacio") {
      articulos.push(elemento);
    }
  }
  return articulos;
}

Este bloque de código para nuestro intérprete se verán algo como lo siguiente:

function obtenerMisArticulos(lista) {
  var articulos;
  var i;
  var elemento;
  articulos = [];
  for (i = 0; i < lista.length; i++) {
    elemento = lista[i];
    if (elemento.autor == "Horacio") {
      articulos.push(elemento);
    }
  }
  return articulos;
}

Como estamos usando el tipo de variable var, todas las declaraciones suben al inicio de la funcion en la cual fueron declaradas(*), guardando en memoria su valor para poder acceder a ellas desde cualquier parte de la función. Todo esto tiene que ver con los contextos de ejecución.

Contextos de Ejecución o “Scope”

Los contextos de ejecución (o scope) son creados cada vez que se ejecuta una función. Se crea un scope por cada función. Veamos un ejemplo simple:

function Bienvenida(nombre) {
  var saludo = "Hola " + nombre;
  console.log(saludo);
}

Bienvenida("Lauro"); // 👈🏼 creamos un scope para `Bienvenida`

El contexto de ejecución se crea para almacenar las variables que se utilizan dentro de la misma. Hay dos fases en los contextos de ejecución: Fase de Creación y fase de Ejecución.

En la fase de creación occurren 4 cosas:

En la fase de Ejecución el intérprete irá evaluando línea por línea hasta llegar al final de la función. Lo bueno es que como todas nuestras declaraciones var han sido “subidas” al inicio de la función, podríamos utilizar las variables incluso antes de la línea en la que las declaramos.

No solo var sufre del hoisting…

Las declaraciones con var no son las únicas que sufren el efecto del Hoisting, las declaraciones de funciones tambien son afectadas, de ahí que podemos llamar a estas funciones incluso antes de ser declaradas. A diferencia de las declaraciones con var que les asigna el valor undefined, las funciones suben por completo.

Todo esto no se podría hacer si usamos una expresión de función en vez de una declaración. En una expresión de función lo que “sube” es justo su declaración y no la función en sí. Hay que tener cuidado con ésto!

saludar("Matías"); // ✅ Válido

function saludar(nombre) {
  var saludo = "Hola " + nombre;
  console.log(saludo);
}

// ------------------

saludar2("Horacio"); // 🚫 No válido

var saludar2 = function saludar(nombre) {
  var saludo = "Hola " + nombre;
  console.log(saludo);
};

Otra propiedad que no sufre del Hoisting son las clases en JavaScript. No podemos inicializar una instancia de una clase antes de la declaración de la misma.

var estudiante = new Persona();

class Persona {
  // ...
}

// 🚫 Error!

¿Qué otras cosas son afectadas por el Hoisting?


Lo que me gustaría que recuerdes cuando veas la palabra Hoisting en JavaScript es que tengas claro que la declaración y la inicialización de variables y funciones ocurre en momentos distintos, incluso cuando los escribes en la misma línea.

(*) - En realidad no es que cambie nuestro código, pero el efecto que ocaciona es el de “subir” las declaraciones