Al trabajar con variables en programación, es fundamental entender el ámbito de la variable. Este es uno de los temas que más cuesta entender a muchos programadores (cuando, en realidad, es muy sencillo).
El ámbito de una variable es el contexto en el que una variable existe y puede ser utilizada. Fuera de su ámbito, la variable no existe (y por tanto, no podemos usarla).
Al igual que vimos al ver el ciclo de vida de un programa, una variable tiene su propio “ciclo de vida”. En general, durante nuestro programa,
- Declaramos / creamos la variable
- Usamos la variable
- La variable se destruye
La zona donde podemos usar la variable es su ámbito. En general (¡spoiler!) en prácticamente todos los lenguajes modernos la variable existe desde que se crea, hasta que se acaba el bloque en que está definida .
Resulta intuitivo pensar que no podemos usar una variable antes de crearla (aunque veremos que algunos lenguajes son especialitos, ¿verdad JavaScript?).
El otro factor que falta ¿Cuándo deja de existir la variable? Pues salvo que la liberemos manualmente, la variable se destruye automáticamente al salir del bloque donde se crea.
Así que el ámbito de existencia de la variable, incluye todas las instrucciones y bloques anidados que se encuentren entre la creación y la destrucción de la variable, que por lo normal se produce al acabar el bloque en el que se creó.
Lo qué, puesto en forma de código, es algo como lo siguiente:
// aqui NO está disponible ❌
{
// aqui NO está disponible ❌
int miVariable = 5;
// aqui SI está disponible ✔️
{
// en este bloque interior TAMBIEN esta disponible ✔️
}
}
// aqui NO está disponible ❌
Ejemplos de ámbito de variables en distintos lenguajes
Vamos a ver el ámbito de la variable en distintos lenguajes
En C#, el ámbito de una variable puede ser un bloque de código, una función o toda la clase:
class EjemploClase
{
int variableDeClase = 5;
void EjemploFuncion()
{
int variableDeFuncion = 10;
if(condition)
{
int variableDeBloque = 15;
}
}
}
Aquí
variableDeClase
estaría disponible en toda la claseEjemploClase
variableDeFuncion
estaría disponible dentro de la funciónEjemploFuncion
variableDeBloque
estaría disponible dentro del condicional
Este comportamiento es más o menos el estándard en todo lenguaje de programación.
Por ejemplo, en Python
def ejemplo():
variableDeFuncion = 10
if True:
variableDeBloque = 15
print(variableDeBloque)
print(variableDeFuncion)
JavaScript merece su propia “mención honorífica” al hablar del ámbito de las variables. En sus primeras versiones tuvo la “genial idea” de ser diferente a los demás (spoiler, no salio bien 😅)
Explicarlo en profundidad lo dejo para cuando haga un curso de JavaScript pero, en resumen, al principio las variables en JavaScript tenían ámbito de función, en lugar de bloque (pausa para aplausos 👏👏).
if (true) {
var x = 20; // ¡¡esto tiene de ámbito la funcion, no el if 😱
}
Eso daba lugar a muchas situaciones raras e inesperadas. Daba tantos problemas, que en siguientes versiones tuvieron que intentar subsanarlo introduciendo let y const, que funcionan en ámbito de bloque.
if (true) {
let x = 20; // esto tiene de ámbito el if
}
En resumen, son pocos los lenguajes en los que el ámbito de la variable no sea el bloque (así que se me ocurran, BASIC y JavaScript). Pero como JavaScript es tan utilizado y tan importante, parecía importante remarcarlo.
Variables globales y variables locales
En muchos lenguajes de programación es posible definir variables globales, que se pueden acceder desde cualquier parte del programa.
Por ejemplo, supongamos un programa de Arduino (que está basado en C++)
int globalVariable = 10; // Declaración de una variable global
void setup() {
}
void loop() {
// Mostrar el valor de la variable global en el monitor serial
Serial.println(globalVariable);
}
Por ejemplo aquí tendríamos un ejemplo de variables globales en Python
x = 10
def myfunc():
# Mostrar el valor de la variable global en la consola
print(x)
Aunque Python también tiene sus cositas “especialitas”. Si quisiéramos modificar el valor de x
, en lugar de sólo consultarlo, deberíamos usar la palabra reservada global
. Pero eso, para cuando haga un curso de Python.
En JavaScript
let globalVariable = 10;
function updateGlobalVariable() {
// Mostrar el valor de la variable global en la consola
console.log(globalVariable)
}
En general, casi todos los lenguajes tienen funciones globales implementadas, de una u otra forma, o con sus peculiaridades de lenguaje. Pero las tienen.
Buenas prácticas Consejos
Se suele decir que emplear variables globales es una mala práctica. Bueno, no siempre es una mala práctica. De hecho, no es solo que tengan su utilidad, es que son necesarias para que funcione el programa (el punto de entrada, sin ir más lejos, es una variable global).
Si os paráis a pensar, en realidad no hay ninguna diferencia conceptual entre una variable global y una variable local. Una variable global también tiene su ámbito. Solo que como está fuera de todo bloque, su “bloque” es el programa entero.
Lo que si es cierto es que abusar de las variables globales es una mala práctica. En general, deberemos usar siempre que sea posible variables locales, y usar variables globales en los pocos casos donde realmente es imprescindible.
Veamos un ejemplo muy sencillo, en una máquina hipotética que lee una temperatura y usa una función para modificarla.
int rawTemperature; // Declaración de una variable global
void correct_temperature() {
rawTemperature *= 2; // Hacer cualquier cosa con tu variable global
}
void loop() {
rawTemperature = 10;
correct_temperature();
}
Subir todas las variables como globales es bastante guarr… bueno ya me entiendes. Es muchísimo mejor si en lugar de eso, usamos variables locales, y cada función tiene definidos los parámetros que necesita y que devuelve.
int correct_temperature(int temperature) {
return temperature * 2;
}
void loop() {
int rawTemperature = 10;
int corrected_temperature = correct_temperature(rawTemperature);
}
A la larga, el código es mucho más mantenible, mejor aislado, reusable, etc etc
Explicación bajo nivel Avanzado
Vamos a intentar explicar de forma resumida como funciona el ámbito de una variable y los motivos de que exista, a bajo nivel.
En lenguajes de compilados como C++ cuando creamos una variable dentro de un bloque, esta se guarda en el stack. Independientemente de que el “contenido” de la variable vaya al heap, la variable en sí siempre va al stack.
Después el bloque hace sus acciones y sus cositas. Cuando se finaliza el bloque, la pila debe de quedar limpia y en estado neutro. Durante el proceso se eliminan las variables locales.
Es decir, a bajo nivel, el hecho de que las variables siempre estén adscritas al bloque en el que son creadas, *es una imposición debido a la forma en la que funciona el procesador.
En lenguajes semi-compilados como Java o C# o interpretados como Python, no tendría porqué ser así. Aquí el gestor de memoria o el intérprete, podrían decidir guardar la variable tras la finalización del bloque.
De hecho, hemos visto que JavaScript, al principio, no cumplía esta normal… y así les fue.
Sin embargo, se ha seguido con las mismas normas porque resultan intuitivas y prácticas. El hecho de que una variable tenga carácter local y deje de existir cuando sale de su ámbito, parece que tiene sentido ¿no?
Esto favorece la reutilización de código y disminuye la posibilidad de errores. Por tanto, los lenguajes de programación más ‘modernos’ siguen manteniendo las mismas normas aunque, a diferencia de los lenguajes más de bajo nivel, estrictamente no se verían obligados a cumplirla.