JavaScript Avanzado

Entendiendo el Scope

Posted by Juan Valera on Monday, February 25, 2019

Introducción

En ciencias de la computación, el Scope es la región de un programa de computadora donde el Binding (ligadura) o la asociación de un nombre a una entidad (puede ser una variable o función), son válidos. 

En JavaScript existen los siguientes tipos distintos de Scope:
  • Function Scope (Scope de función)
  • Block Scope (Scope de bloque)

Hasta ECMA6, el Scope de función era la única forma de Scope existente en JavaScript; todas las variables y todas las funciones simplemente seguían las reglas de Scope de función.


El Scope de bloque fue introducido en ECMA6 y usados únicamente por las variables declaradas con las nuevas palabras reservadas de declaracion let y const.

Scope de función

Scope de función

En  JavaScript, el Scope de función es creado dentro de las funciones. Al declarar una función, un nuevo Scope de bloque es creado dentro del cuerpo de esa función. Las variables que son declaradas dentro del nuevo Scope de función no se pueden acceder desde el Scope padre, sin embargo, el Scope de función tiene acceso a las variables declaradas en el Scope padre.

Sé que quizás parezca un poco confuso, por lo tanto a modo de ejemplo, analicemos la siguiente imagen:

Como podemos ver en la imagen, desde la función hija childFn(), tenemos acceso tanto a la variable de su Scope "let varInChildScope", como la variable del Scope padre paretnFn() "let varInParentScope".


Desde el Scope padre paretnFn(), sólo tenemos acceso a su variable "let varInParentScope", mas no a la variable declarada en el Scope hijo "let varInChildScope".

Para crear una variable de Scope de función, debemos declararla con la palabra reservada var. Ejemplo: var miVariable = 34;

                        
                                
var edadActual = 34;

function miEdad() {

    var edadAnterior = 33;

    console.log( edadActual ); // Salida esperada: 34

    console.log( edadPasada ); // Salida esperada: 33
}

miEdad();

console.log( edadPasada ); // Error de referencia

Scope padre

Es simplemente el Scope de la sección del código donde la función fue definida. Usualmente es el Scope global, sin embargo, en algunos casos puede ser muy útil crear funciones anidadas (funciones dentro de otras funciones). En ese caso el Scope padre de las funciones anidadas, sería la función donde están definidas.

En el ejemplo anterior el Scope de función es el Scope creado dentro de la función "miEdad()". El Scope padre es el Scope global, es decir, donde está declarada la función.

El Scope padre, es el bloque de código donde está declarada la función. No es el bloque de código donde es llamada la función.

Scope de bloque

Un nuevo Scope de bloque en JavaScript es creado únicamente con llaves "{}". Este par de llaves puede ser colocado en cualquier parte de nuestro código para declarar un nuevo bloque. Si una declaración, ciclo iterativo, función u otro tiene llaves declaradas entonces ellos tienen su propio Scope. Esto incluye cualquier par de llaves creados que no tengan asociado ninguna palabra reservada (while, if, etc).

                        
                                
function scopeEjemplo() {
    
    // Scope de bloque 1
    
    for ( let i = 0; i < 10; i++ )
    {
        /* Scope de bloque 2 */ 
    }

    if ( true ) 
    { 
        /* Scope de bloque 3 */ 
    } 
    else 
    { 
        /* Scope de bloque 4 */ 
    }

    // El par de llaves sin palabras clave crean un Scope de bloque
    { 
        /* Scope de bloque 5 */
    }

    // Scope de bloque 1
}

Las variables declaradas con las palabras reservadas let y const tienen Scope de bloque. Cuando una variable es declarada con Scope de bloque, no tiene el mismo hoisting de variable, que las variables creadas en un Scope de función. Las varibles de bloque no son "levantadas"(hoisted) al inicio del scope y no pueden ser utilizadas hasta no ser declaradas. Esto significa que las variables creadas con Scope de bloque están sujetas a la Zona Muerta Temporal o TDZ por sus siglas en inglés (Temporal Dead Zone)


La TDZ es el periodo entre cuando el Scope es ingresado y la variable es declarada. Este periodo termina cuando la variable es declarada más que asignada. Ver ejemplo:

                        
                                
// console.log( myVariableLet ); // Devuelve error:  "ReferenceError"
let miVariableLet;
    
console.log( miVariableLet ); // Salida: undefined

miVariableLet = 34;

console.log( miVariableLet ); // Salida: 34

Si una variable es accedida dentro de la TDZ, entonces un error de runtime será devuelto. Esto es importante por que nos permite construir un código mucho más robusto con menos cantidad de errores semánticos derivados de la declaración de variables.

Para entender mejor el Scope de bloque, podemos ver la siguiente lista:

  • Scope de función
    • Scope de bloque para cada función.
    • Variables definidas con var.
    • Hoisting de variables.
  • Scope de bloque
    • Scope de bloque para los pares de llaves "{}".
    • Variables definidas con let y const.
    • Zona muerta temporal (TDZ).

En conclusión, los Scopes nos proveen una forma de separar variables y restringir el acceso entre los bloques de código. Los nombres identificadores de variables pueden ser reutilizados entre los Scopes de bloque. Todos los nuevos Scopes de bloque que son creadas pueden acceder al Scope padre, o al Scope donde fueron creados o definidos. Las variables pueden ser agregadas al Scope de función con la palabra reservada var, y estas variables son hoisted al inicio del Scope. El Scope de bloque es una nueva funcionalidad de ES6. Un nuevo Scope de bloque es creado por cada par de llaves "{}". Las variables son agregadas al Scope de bloque con las palabras reservadas let y const, estas variables no son hoisted y están sujetas a la Zona muerta temporal (TDZ).

Cualquier comentario o duda pueden contactarme a través de la sección Contact.

Juan Valera.