¿Como funciona el Scope de Javascript?

El Scope se define como el conjunto de reglas que determinan cómo el motor/engine puede buscar una variable por el nombre que la identifica y encontrarla en el alcance que la contenga.

Sabemos que en Javascript tenemos un Scope o un alcance de la variable en varios niveles:

  • Scope global
  • Scope de una función
  • Scope de bloque

Ok ok, bueno, primero vamos a entender que es lo que ocurre cuando declaramos una variable de la manera siguiente:

var a = 2;

Para ello necesitamos conocer los componentes de Javascript que intervienen y como se comunican entre si al declarar una variable.

  • Motor/Engine: es el responsable de la compilación y ejecución de principio a fin de nuestro programa JavaScript (V8 es el motor utilizado en Chrome y en Node.js).
  • Compilador: Si bien ya les había platicado que Javascript es un lenguaje compilado, se encarga de manejar todo el trabajo sucio de análisis y generación de código.
  • Scope: recopila y mantiene una lista de búsqueda de todos los identificadores declarados (variables), y aplica un conjunto estricto de reglas sobre cómo estos son accesibles para el código que se está ejecutando actualmente.

Lo que hará el Compilador con var a = 2; es primero dividirlo en tokens (tokenizing/lexing), luego los analizará en un árbol (ATS), pero cuando llegue a la generación de código procederá de la siguiente manera:

  1. Al encontrarse con var a, el Compilador pregunta al Scope si ya existe una variable a en el scope actual (donde nos encontremos ya sea; a nivel global, función o bloque). Si es así, el compilador omite esta declaración y sigue adelante. De lo contrario, el Compilador solicita a Scope que declare una nueva variable llamada a en el scope actual.
  2. El Compilador entonces produce código para que el Motor ejecute más adelante, para manejar la asignación a = 2. El código que el Motor ejecuta, primero preguntará a Scope si hay una variable llamada a accesible en el scope actual. Si es así, el motor utiliza esa variable. Si no, el motor busca en otra parte (en un scope superior).

En caso de que el motor no encuentre la variable se producirá un error. ¿Pero como es que el motor busca las variables?

La forma en la cual el motor encuentra las variables es la siguiente:

  1. El motor levanta su mano derecha 🤚
  2. El motor levanta su mano izquierda ✋
  3. Ok realmente esto es muy importante, debido a que el motor no fué a pre-escolar y necesita ayuda para saber si su mano es derecha o izquierda (Ok no, pero si es importante).

Cuando el Motor ejecuta el código que el Compilador produjo, tiene que buscar la variable a para verificar si se ha declarado y esta búsqueda está consultando el Scope.

El Motor estaría realizando una búsqueda LHS (Left Hand Side ✋) para la variable a en var a = 2;, el otro tipo de busqueda es RHS (Right Hand Side ✋). El lado izquiero y el lado derecho se refiere a el lado de una operación de asignación (Hey, les dije que lo de las manitas era importante 👐).

Una búsqueda LHS se hace cuando aparece una variable en el lado izquierdo de una operación de asignación y una búsqueda RHS se hace cuando aparece una variable en el lado derecho de una operación de asignación.

Bueno, en el caso de RHS no precisamente significa, lado derecho de una asignación (Right Hand Side), sería mas que nada un No Left Hand Side, o un no a la izquierda. Y si seguimos las siglas podría considerarse algo como Retrieve His/Her Source (ir a buscar el valor de). Y aqui un ejemplo para aclarar esta pequeña confusión:

console.log(a)

La referencia a es una referencia RHS, porque no le estamos asignando nada a a. De hecho, estamos buscando obtener el valor de a, para que el valor de a pueda pasarse al console.log (..).

En cambio, en el caso de var a = 2; la referencia es LHS porque realmente no nos importa cuál es el valor actual, simplemente queremos encontrar la variable como un objetivo para la operación de asignación = 2.

Hay que tomar en cuenta que hay otras maneras en que ocurren las asignaciones, por lo que es mejor pensar sobre esto como:

  • ¿Quién es el objetivo de la asignación? (LHS)
  • ¿Quién es la fuente de la asignación? (RHS)

Imaginemos como es una conversación entre el Motor y el Scope:

Motor: Amigo Scope, te invito una copa!!
Scope: No tomo, gracias!!
Motor: No tomas bien te invito a un cafe.
Scope: Bueno!!
Motor: Que quiero recordar la epoca loca, de ayer cuando parseabamos Hexadecimal.

Ok ok, pero no tan casual!! 😅

Imaginemos una conversación entre el Motor y el Scope considerando la siguiente función:

function holis(a) {
    console.log(a); // 2
}
holis(2); 

Motor: Que onda mi Scope oye, tengo una referencia RHS para holis, ¿no te suena?.
Scope: Yep, el Compilador lo declaró hace apenas un segundo. Es una función, aqui tienes.
Motor: Chido, gracias! estoy ejecutando holis.
Motor: Oye Scope mira, tengo una referencia de LHS para a, ¿has oído hablar de a?
Scope: Simón, el Compilador lo declaró como un parámetro para holis hace poco, aqui tienes.
Motor: Eres la onda, Scope, Gracias de nuevo!, ahora es tiempo para asignar 2 a a.
Motor: Oye mi Scope, me da pena molestarte de nuevo, pero necesito una búsqueda de RHS para console, ¿has escuchado de él?.
Scope: No hay pedo, son los gajes del oficio, sí tengo console, está integrado, aqui lo tienes.
Motor: Perfecto!!. Buscando el log(..). Ya estuvo, es una función.
Motor: Oye Scope, ya me cae que es lo ultimo que te pido, ¿puedes ayudarme con una referencia de RHS a a. Creo que recuerdo esa referencia, pero sólo quiero comprobarlo.
Scope: Es cierto Motor, La misma variable no ha cambiado. Aqui tienes.
Motor: Eso es todo!!. Pasando el valor de a, que es 2, en log(..).

Ok, si fué medio casual la conversación y mas si se lo imaginan con el acento Chilango de la Ciudad de México!!.

Al final como pudimos ver, al declarar y obtener el valor de una variable en Javascript suceden muchas cosas, a nivel de "comunicación" entre el Compilador, el Engine/Motor y el Scope. Conocer esta parte de Javascript es importante debido a que los desarrolladores que comienzan a programar en Javascript se encuentran con resultados inesperados en su código. Ademas de que es la base para entender como funciona el Lexical Scope (Scope Lexico) de Javascript del cual tambien les contaré mas adelante!!.