Ámbitos o contextos

Región en la que una variable existe


Un tema complejo en el mundo de la programación es el de los ámbitos, scopes o contextos. Cuando comenzamos a aprender sobre variables y constantes, es bueno aprender y entender bien los ámbitos, ya que es la zona de alcance que tiene una variable.

En principio, existen dos ámbitos muy bien definidos:

  • Ámbito global: Existe a lo largo de todo el programa o aplicación.
  • Ámbito local: Existe sólo en una pequeña región del programa.

Normalmente, esto se determina muy fácilmente observando las llaves { y } (curly braces), que abren un nuevo ámbito o contexto.

Ámbitos de variables

Cuando inicializamos una variable al principio de nuestro programa y le asignamos un valor, ese valor generalmente está disponible a lo largo de todo el programa. Sin embargo, esto puede variar dependiendo de múltiples factores. Se conoce como ámbito de una variable a la zona donde esa variable existe.

Por ejemplo, si consultamos el valor de una variable antes de inicializarla, no existe:

console.log(e);  // Uncaught ReferenceError: e is not defined

let e = 40;
console.log(e);  // 40 (existe porque ya se ha inicializado en la línea anterior)

En el ejemplo anterior, el ámbito de la variable e comienza a partir de su inicialización y "vive" hasta el final del programa. A esto se le llama ámbito global y es el caso más sencillo. Observa que en la primera línea la variable e no existe y da un error, pero tras la linea del let, si devuelve su valor porque ahí si existe.

Sin embargo, esto se puede ir complicando a medida que vamos abriendo llaves de instrucciones como if, else, for o while. Observa el siguiente ejemplo:

let a = 1;
console.log(e);  // Uncaught ReferenceError: e is not defined

if (a == 1) {
  let e = 40;
  console.log(e);   // 40, existe
}

console.log(e);  // Uncaught ReferenceError: e is not defined

Observa que la variable a existe en un ámbito global, mientras que la variable e sólo existe en el interior del if: es un ámbito local.

Scope o ámbitos: Simba meme

Es puede volverse un poco más complicado dependiendo de si utilizar let, const o var, o dependiendo de la anidación. Lo iremos viendo a medida que avancemos.

Ámbitos de variables: let y const

En las versiones modernas de Javascript (a partir de ECMAScript 2015), se introduce la palabra clave let en sustitución de la antigua var para declarar variables y const para declarar constantes. Tanto con let como con const, estaremos utilizando los ámbitos clásicos de programación: ámbito global y ámbito local.

Veamos otro ejemplo, esta vez utilizando un bucle for:

console.log("Antes: ", p);          // En este punto, p no está definida
for (let p = 0; p < 3; p++) {
  console.log("Valor de p: ", p);   // Aquí, p estará definida como 0, 1, 2
}
console.log("Después: ", p);        // En este punto, vuelve a no estar definida

Varios detalles:

  1. Utilizando let en el bucle for, la variable p sólo está definida dentro del bucle (ámbito local)
  2. Observa que tanto antes como después del bucle, p no existe.

Aunque omitamos las llaves del for (en este caso es posible porque sólo contiene una línea), los ámbitos siguen existiendo. Hacemos referencia a las llaves porque se ve mejor, pero lo que importa es si se declara la variable dentro del bucle for o no.

Ámbitos de variables: var (legacy)

Aunque declarar variables con var ya se considera legacy (obsoleto) y no debe usarse, si te interesa saber un poco más por si te lo encuentras, en este enfoque tradicional de Javascript, existen ámbitos diferentes: el ámbito global y el ámbito a nivel de función.

La diferencia se puede ver claramente en el uso de un bucle for con var (a diferencia del que vimos anteriormente con let):

console.log("Antes: ", p);          // En este punto, p vale undefined
for (var p = 0; p < 3; p++) {
  console.log("Valor de p: ", p);   // Aquí, p estará definida como 0, 1, 2
}
console.log("Después: ", p);        // Después: 3 (WTF!)

Esto es algo que a un programador acostumbrado a otros lenguajes con ámbitos tradicionales le puede explotar la cabeza. Si utilizamos var la variable p sigue existiendo fuera del bucle, aunque haya sido declarada en su interior. Esto ocurre porque en ese caso se usa un ámbito a nivel de función.

Veamos este otro ejemplo (necesitaras conocer el concepto de función, por lo que si no lo conoces, quizás necesites volver más tarde cuando hayas visto ese tema):

var a = 1;
console.log(a); // Aquí accedemos a la "a" global, que vale 1

function x() {
  console.log(a); // En esta línea el valor de "a" es undefined
  var a = 5; // Aquí creamos una variable "a" a nivel de función

  console.log(a); // Aquí el valor de "a" es 5 (a nivel de función)
  console.log(window.a); // Aquí el valor de "a" es 1 (ámbito global)
}

x(); // Aquí se ejecuta el código de la función x()
console.log(a); // En esta línea el valor de "a" es 1

En el ejemplo anterior vemos que el valor de a dentro de una función no es el 1 inicial, sino que estamos en otro ámbito diferente donde la variable a anterior no existe: un ámbito a nivel de función. Mientras estemos dentro de una función, las variables inicializadas en ella estarán en el ámbito de la propia función.

Estamos usando el objeto especial window para acceder directamente al ámbito global independientemente de donde nos encontremos. Esto ocurre así porque las variables globales se almacenan dentro del objeto window (la pestaña actual del navegador web). Hoy en día, en lugar de utilizar window sería preferible utilizar globalThis.

Sin embargo, si eliminamos el var de la línea var a = 5 del ejemplo anterior, observa la diferencia:

var a = 1;
console.log(a); // Aquí accedemos a la "a" global, que vale 1

function x() {
  console.log(a); // En esta línea el valor de "a" es 1
  a = 5; // Aquí creamos una variable "a" en el ámbito anterior

  console.log(a); // Aquí el valor de "a" es 5 (a nivel de función)
  console.log(window.a); // Aquí el valor de "a" es 5 (ámbito global)
}

x(); // Aquí se ejecuta el código de la función x()
console.log(a); // En esta línea el valor de "a" es 5

En este ejemplo se omite el var dentro de la función, y vemos que en lugar de crear una variable en el ámbito de la función, se modifica el valor de la variable a a nivel global. Dependiendo de donde y como accedamos a la variable a, obtendremos un valor u otro.

Siempre que sea posible se debería utilizar let y const, en lugar de var. Declarar variables mediante var se recomienda en fases de aprendizaje o en el caso de que se quiera mantener compatibilidad con navegadores muy antiguos utilizando ECMAScript 5, sin embargo, hay estrategias mejores a seguir que utilizar var en la actualidad.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev