Una de las particularidades de JavaScript es lo que se conoce comúnmente como hoisting. Dicha característica consiste en que con independencia de donde esté la declaración de una variable, ésta es movida al inicio del ámbito al que pertenece. Es decir, aunque nuestro código sea como el siguiente:
function foo() {
console.log(x);
var x=10;
}
Realmente se tratará a todos los efectos como si hubiésemos escrito:
function foo() {
var x;
console.log(x);
x=10;
}
Por supuesto, al ejecutar el código este imprime “undefined” en pantalla, pero no es porque la variable x no esté definida al momento de ejecutar el console.log, es porque no tiene valor (y las variables sin valor asignado se les asigna el valor de undefined). Esto hace que el hoisting pase indvertido muchas veces, pero debemos tener cuidado con él. Por ejemplo, supongamos el siguiente código:
var x='global value';
function foo() {
console.log(x);
var x='local value';
console.log(x);
}
foo();
Uno podría esperar que se imprimiese primero “global value” y luego “local value”, ya que parece que cuando se ejecuta el primer console.log(x) la variable x local todavía no existe, por lo que se imprimiría el valor de la variable x global. Pero no ocurre esto. En su lugar dicho código muestra “undefined” y luego “local value”. Eso es debido a que gracias al (o por culpa del) hoisting es como si realmente hubiésemos escrito:
var x='global value';
function foo() {
var x;
console.log(x);
x='local value';
console.log(x);
}
foo();
Ahora se puede observar claramente como la variable x local oculta a la variable x global incluso antes del primer console.log.
Es importante además recalcar que, a diferencia de otros lenguajes, el código dentro de las llaves de un if o de un for no abre un ámbito nuevo. Supongamos un código como el que viene a continuación:
function foo() {
var item={v:'value'};
for (var idx in [0,1]) {
var item = {i: idx};
console.log(item);
}
console.log(item);
}
foo();
Este código parece que tenga que imprimir {i:0}, {i:1} (al iterar dentro del for) y luego {v:'value'} (el valor de la variable item de fuera del for). Pero realmente la declaración de la variable item dentro del for se mueve fuera de este, ya que el bloque for no declara un nuevo ámbito de visibilidad. De este modo el código es equivalente a:
function foo() {
var item; // Primer item declarado
var item; // Segundo item declarado dentro del for
var idx; // Variable idx declarada en el for
item={v:'value'};
for (idx in [0,1]) {
item = {i: idx};
console.log(item);
}
console.log(item);
}
foo();
Declarar dos veces una misma variable en JavaScript no da error y ahora se puede ver como la salida real de nuestro programa será {i:0}, {i:1} y luego{i:1} otra vez. Fíjate que lo mismo ocurriría en el caso de la variable idx si hubiese otra variable idx declarada antes del for.
En resumen, el hoisting es una característica de JavaScript que aunque muchas veces pasa inadvertida debemos comprender, para así entender algunos comportamientos del lenguaje que de otro modo nos parecerían totalmente erráticos.