4

¿Declarar una variable dentro de un bucle sobreescribe la definición anterior en la siguiente iteracion al no haber otra variable a nivel de código con el mismo nombre?

Hola podrían decirme si mi interpretación es correcta?

Es decir, tengo el siguiente codigo en el que declaro una variable dentro de un bucle

for(let i=0; i<10; i++){
  let mi_variable = 9;
  console.log(mi_variable);
}

Tengo entendido que en ningún lenguaje de programación no se permiten variables con el mismo nombre en el mismo scope, por ejemplo en este caso:

let mi_Variable = 9
let mi_Variable = 9

¿Mientras que en el ejemplo anterior en donde tengo una variable declarada en un bucle, en la segunda iteracion del bucle la variable ya existe pero al no haber otro identificador en el código con el mismo nombre, lo único que hace el interprete sobreescribir la definición de variable que había anteriormente en vez de lanzar un error?

¿Esto sucede en todos los lenguajes de programación?

¿entonces no es lo mismo que tener dos sentencias de declaración de variable con el mismo nombre a tener una sola dentro del bucle?.

¿si bien es cierto que que en la segunda iteracion la variable ya existe, Al pasar de nuevo por la instrucción, el interprete verificara en el scope si no hay alguna otra caja (variable) con ese nombre?, y ¿entonces el interprete al no ver en el código(en el scope para se mas exactos) otra variable con ese nombre lo que hace es machacar la variable que existia en la anterior iteracion y definir una nueva con el valor?

Estoy en lo correcto?

Juan Mata
  • 121
  • 2
  • 1
    Relacionada: [var, let, const o nada en Javascript](https://es.stackoverflow.com/questions/106042/var-let-const-o-nada-en-javascript/106067#106067) – Pablo Lozano Jun 03 '19 at 08:06

2 Answers2

3

Tu pregunta es bastante interesante desde el punto de vista del comportamiento del lenguaje. Trataré de responderte usando mis conocimientos del lenguaje y haciendo un repaso básico del concepto de ámbito (scope) y la definición de variable.

ES5 y anteriores

En versiones previas de JavaScript, (ES5 y anteriores) no existía el ámbito de bloque (block scope) y tampoco existían las palabras claves let y const, por lo tanto las variables declaradas dentro de un bloque (por ejemplo un bucle for) pertenecían al ámbito de la función que contenía el bucle, o al ámbito del script donde dicho bucle era ejecutado, o al ámbito global si el bucle era ejecutado allí.

Un ejemplo de este comportamiento sería el siguiente:

function miFuncion() {
  for(var i = 0; i < 5; i++) {
    var miVariable = i; // <- El ámbito de miVariable es miFuncion
  }
  console.log('miVariable es: ', miVariable);
}

var miVariable = 'string'; // el ámbito de miVariable es el ámbito Global

miFuncion(); // <-Imprime el valor de la variable dentro del ámbito de miFuncion

console.log('miVariable es: ', miVariable); // imprime el valor de la variable del ámbito Global

Podemos apreciar que hay una diferencia en el valor de miVariable, una pertenece al ámbito global y la otra al ámbito de la función. Por lo tanto son dos variables diferentes, una sólo puede ser accedida desde dentro de la función y fuera de esta no es posible acceder.

¿Pero qué sucede cuando el bucle es llamado directamente en el ámbito global? Veamos un ejemplo:

var miVariable = 'string'; // miVariable pertenece al ámbito Global
console.log('miVarible es: ', miVariable);

for(var i = 0; i < 5; i++) {
  var miVariable = i; // miVariable es redeclarada y reasignada (sigue perteneciendo al ámbito global)
}

console.log('miVarible es: ', miVariable); // se imprime el valor asignado dentro del bucle

Como podemos apreciar, el valor de miVariable es re-declarado y reasignado dentro del bucle, ya que la misma pertenece al ámbito global al haber sido declarada inicialmente allí.

Es así que en Javascript las variables declaradas con var tienen un alcance de función o script. No tienen alcance de bloque. Y de cierta forma podemos decir que pueden ser re-declradas (un forma fácil de decir que las re-declaraciones son ignoradas dentro del mismo ámbito, como veremos más adelante).

En versiones anteriores de Javascript, sólo teníamos la palabra reservada var para declarar una variable. Si por cualquier motivo olvidábamos declarar una variable usando var, la misma sería automáticamente elevada (hoisted) al ámbito al que perteneciera la sentencia, fuera este un ámbito de función o un ámbito global, incluso si la misma era usada dentro de un bucle en una función.

Por ejemplo la siguiente instrucción era totalmente válida en una versión antigua de Javascript:

function miFuncion() {
    for(var i = 0; i < 5; i++) {
        // miVariable no es declarada usando var
        // por lo tanto será elevada al ámbito de la función
        miVariable = i;
    }
// dado que en el ámbito de la función tampoco ha sido declarada
// miVariable, la misma será elevada al ámbito global
}

miFuncion();

// ahora en el ámbito global existe miVariable
alert(miVariable); // muestra: 4

Podríamos probar dicho código en Javascript Tester

Ya que hemos entendido el ámbito de declaración de variables en JS, cuando no existía en el lenguaje el ámbito de bloque, veamos que ocurre con la siguiente sentencia:

var miVariable = 'Hola';
var miVariable = 'Adiós';

console.log(miVariable);

¿Cómo es posible que podamos re-declarar una variable?

Esta característica fue una decisión de diseño del lenguaje, por lo tanto, y dado que Javascript fue creado en poco tiempo (10 días según muchos autores), se eligió un sistema de variables no tipadas (véase tipado fuerte) Así una variable en JS no sólo podía ser reasignada, sino que su tipo podía ser intercambiado y además podía ser re-declarada.

En realidad, no se trata de una re-declaración (o como tu lo llamas en tu pregunta: machacar), lo que sucede es que el compilador al analizar el código reserva espacio de memoria para la primera variable que encuentre declarada con la palabra reservada var, si más adelante dentro del mismo ámbito encuentra nuevamente una declaración usando var con el mismo nombre de variable, esta declaración es ignorada y se utiliza el mismo espacio de memoria que el usado para la primera declaración.

// se hace referencia a una variable que aún no ha sido declarada
// el compilador analiza todo el script, si no hay una declaración
// válida para miVariable (usando var) arrojará un error
// en cambio si la hay, reservará espacio de memoria y le asignará como valor undefined
console.log('miVariable es: ', miVariable);

// se declara miVariable y no se le asigna ningún valor
// el compilador le asignará undefined
var miVariable;
console.log('miVariable es: ', miVariable);


// esta nueva declaración será ignorada, se usará la misma
// dirección de memoria de la primera declaración
// el valor será actualizado a un string
var miVariable = 'Un string';
console.log('miVariable es: ', miVariable);


// nuevamente esta declaración es ignorada
// al no asignarle valor, se usará el valor que ya tenía
// en este caso: un string
var miVariable;
console.log('miVariable es: ', miVariable);

ES6 (ES2015), let, const, y ámbito de bloque

Tal vez una de las cosas que hicieron impopular a Javascript en sus inicios fue precisamente la forma en la que se manejaban las variables dentro del lenguaje.

Imaginemos intentar mantener un código con algunos miles de líneas y olvidar declarar una variable usando var en una función, y que ya hubiésemos declarado una variable con el mismo nombre en otro lugar de nuestro programa fuera de nuestra función. Al ser tan permisivo el sistema de tipado, no nos daríamos cuenta del error hasta que el programa se ejecutara y comenzara a dar resultados no esperados.

Con el auge de la web y la aparición de aplicaciones para la misma, se añadieron nuevas características al lenguaje, a la vez que se mantuvo la compatibilidad con las versiones anteriores.

Entre dichas características se encuentran el ámbito de bloque y las palabras reservadas let y const.

Como vimos anteriormente, si realizamos una asignación a una variable sin haberla declarado con anterioridad, el resultado mostrado es: undefined.

//llamado a una variable que aún no ha sido declarada
console.log(x); // <- imprime undefined
var x = 100;

Sin embargo, si no realizamos la declaración usando var obtendremos un error de referencia, ya que no se ha reservado espacio de memoria para una variable llamada x durante la compilación del script.

Es allí donde entran en juego las mejoras realizadas al lenguaje en la especificación ES2015 (ES6).

El introducir un ámbito de bloque, implicaba realizar un cambio profundo en la forma en que se declaraban las variables. Y realizar dicho cambio sobre el comportamiento de las variables declaradas con var significaba romper la compatibilidad con los programas escritos hasta el momento. Es por ello que se introducen las palabras clave let y const. Ambas tienen un alcance dentro de un ámbito de bloque. Además, las variables declaradas usando let o const no pueden ser re-declaradas y únicamente las variables declaradas usando let pueden ser reasignadas.

Es por ello que hacer esto genera un error de tipo SyntaxError:

let miVariable = 'Hola';
let miVariable = 'Mundo';

Uncaught SyntaxError: Identifier 'miVariable' has already been declared at :1:1

Y hacer esto otro genera un error de tipo TypeError:

const miVariable = 'Hola';
miVariable = 'Mundo';

Uncaught TypeError: Assignment to constant variable. at :1:13

Entonces ya tenemos las diferencias marcadas sobre var, let y const.

Ahora, volviendo a tu primera pregunta:

¿Mientras que en el ejemplo anterior en donde tengo una variable declarada en un bucle, en la segunda iteración del bucle la variable ya existe pero al no haber otro identificador en el código con el mismo nombre, lo único que hace el interprete sobreescribir la definición de variable que había anteriormente en vez de lanzar un error?

La respuesta es no. El intérprete no reescribe la variable, ya que como hemos visto, intentar hacer esto causaría un error de sintaxis.

En realidad lo que sucede es que cada iteración es considerada un bloque diferente. Por lo tanto cada variable declarada dentro de un bucle for (o cualquier otro tipo de bucle) tiene alcance dentro del ámbito de bloque, que en este caso es una iteración. Así, el intérprete reservará espacio de memoria para cada variable en cada iteración, y las tratará como variables diferentes.

Por lo tanto ejecutar un bucle tipo for es equivalente a realizar lo siguiente:

{
  let miVariable = 0;
  console.log('miVariable es: ', miVariable);
}
{
  let miVariable = 1;
  console.log('miVariable es: ', miVariable);
}
{
  let miVariable = 2;
  console.log('miVariable es: ', miVariable);
}
{
  let miVariable = 3;
  console.log('miVariable es: ', miVariable);
}
{
  let miVariable = 4;
  console.log('miVariable es: ', miVariable);
}

Como puedes ver, se declara una variable dentro de cada bloque. Su ámbito está supeditado a su bloque y así aunque las variables tienen el mismo nombre, no entran en conflicto unas con otras ya que pertenecen a ámbitos (bloques) diferentes.

¿Pero qué pasa si uso var en vez de let? Dado que var como hemos visto tiene un ámbito de función o script y no de bloque, ¿causaría esto un error?

No, ya que por definición var (a diferencia de let y const) si puede ser re-declarada. Sólo en ese caso en el que var es usada para declarar una variable dentro de un bucle, la variable será elevada (hoisted) al ámbito de la función o script al que pertenezca el bucle. Por lo tanto, en cada iteración, la variable será machacada, como expresas en tu pregunta.

Podemos ver este comportamiento haciendo lo siguiente:

{
  var miVariable = 0;
}
{
  var miVariable = 1;
}
{
  var miVariable = 2;
}
{
  var miVariable = 3;
}
{
  var miVariable = 4;
}

console.log('miVariable es: ', miVariable);

Puedes apreciar que miVariable fue declarada usando var en cada bloque, sin embargo se puede acceder a la misma desde fuera del bloque, ya que fue elevada al ámbito al que pertenece el bloque (en este caso el ámbito global).

Volviendo nuevamente al resto de tus preguntas:

¿Esto sucede en todos los lenguajes de programación?

No, como vimos al principio, no todos los lenguajes tienen un tipado como el de Javascript. Como yo no domino todos los lenguajes que existen, no podría decirte a ciencia cierta en cuáles se puede dar el mismo comportamiento y en cuáles no.

¿Entonces no es lo mismo que tener dos sentencias de declaración de variable con el mismo nombre a tener una sola dentro del bucle?

La respuesta es: depende. Las variables declaradas con var pueden ser re-declaradas dentro del mismo ámbito, en cambio las variables declaradas con let y const (que tienen alcance de bloque) no pueden ser re-declaradas dentro del mismo bloque o ámbito. Dado que una iteración de un bucle es un bloque, cada vez que se realiza la declaración de una variable usando let o const, la misma sólo tendrá alcance dentro de dicha iteración, haciendo que el intérprete de JS reserve un espacio de memoria para cada variable de cada iteración.

¿Si bien es cierto que en la segunda iteración la variable ya existe, Al pasar de nuevo por la instrucción, el interprete verificara en el scope si no hay alguna otra caja (variable) con ese nombre?, y ¿entonces el interprete al no ver en el código(en el scope para ser más exactos) otra variable con ese nombre lo que hace es machacar la variable que existía en la anterior iteración y definir una nueva con el valor?

Esta pregunta se responde con las anteriores. El intérprete no machaca nada, simplemente cada iteración es un bloque, luego las variables declaradas usando let son diferentes para cada iteración.

En el caso que se use var para la declaración, la variable es primero elevada (hoisted) al ámbito al que pertenezca el bucle. Luego, dado que el uso de var permite realizar una re-declaración, entonces es permitido el comportamiento dentro del bucle en cada iteración.

Espero que tus dudas hayan sido aclaradas con esta explicación que intento dar de acuerdo a mis conocimientos y experiencia. Tal vez alguien con más experiencia en este campo pueda aportar más a esta respuesta.

Mauricio Contreras
  • 13,660
  • 3
  • 18
  • 40
-1

He trabajado con VB , C# y Java, ahi si declaras la variable dentro de un ciclo/bucle marcara un error al querer declararla de nuevo en la segunda iteración.

El interprete si verifica la existencia del nombre.

  • 1
    No es correcto, en Java o C# puedes declarar sin problemas una variable dentro de un bucle y su existencia se limitará a dicho bucle – Pablo Lozano Jun 03 '19 at 08:08