Recientemente un viejo colega (@f5inet) me pedía una breve introducción al uso de las variables en Javascript. Explicándole por encima las normas básicas, llegué a uno de los puntos que más confusión despiertan y que son una fuente frecuente de errores para los programadores noveles: el hoisting.
El hoisting («elevación») en Javascript es una de esas particularidades del lenguaje sobre la que la comunidad de desarrolladores no llega a ponerse de acuerdo en si se trata de un bug o una feature.
Para John Resig, manejado correctamente, es un poderoso aliado a la hora de gestionar los ámbitos que alcanzan nuestras variables; para otros como Douglas Crackford, su descuido se convierte inmediatamente en inconsistencias en nuestros códigos y errores potenciales difíciles después de identificar y depurar.
Qué es el hoisting?
En Javascript, cuando se define una variable en el interior de una función, el intérprete interno pasa a ubicarla al comienzo de su contexto (la eleva). Podemos imaginarnos así a nuestras funciones como recipientes de agua hirviendo donde las partículas más pequeñas (las variables) buyen hacia la superficie; el resto de elementos, incluidos los valores de esas variables, son más pesados y se quedan donde están.
Ejemplos
Tomemos el siguiente código:
function foo(){ bar(); var x = 1; } |
Pese a que hemos declarado la variable x un punto concreto del código, el intérprete Javascript mueve dicha declaración a lo más alto de su contexto. Internamente, el código se ejecutaría de la siguiente manera:
function foo(){ var x; bar(); x = 1; } |
La variable es declarada antes que se ejecute cualquier otra acción, lo que puede llegar a ocasionar comportamientos no esperados:
var x = 'Hello World'; // variable global function foo(){ alert( x ); // esperamos el valor global var x = 'New Value'; // redefinimos la variable en contexto local alert( x ); // esperamos el nuevo valor local } foo(); |
Posiblemente este código no funcione como se deseábamos: el primer alert() no devuelve el valor ‘Hello World’ pese a que la variable había sido declarada anteriormente como global. En su lugar, obtenemos un inesperado undefined. La razón es que la posterior declaración de x, internamente se eleva hasta el inicio de su función (buye), por lo que pasa a estar definida de forma local pero sin un valor aún asignado. El anterior código equivale a este mucho más claro:
var x = 'Hello World'; function foo(){ var x; alert( x ); x = 'New Value'; alert( x ); } foo(); |
La recomendación de los expertos es siempre declarar las variables locales al principio de su actual ámbito para evitar errores. De esta forma ganamos además legibilidad del código ya que agrupamos así todas nuestras variables en un mismo lugar permitiéndonos de un vistazo conocer el tipo de datos que esperan:
function foo(){ var myObj = {}; var myArray = []; var number1, number2, number3; var string1, string2, string3; // Some code here... } |
Como apunte final, aclarar que el término hoisting no está definido dentro del actual ECMAScript, sin embargo es comunmente utilizando para describir el particular comportamiento que Javascript hace de las variables en el interior de las funciones.
Muy interesante tu blog.
Lo acabo de descubrir accidentalmente…
Un remedio de la abuela, la que fuma, para evitar accidentes como el de tu segundo ejemplo es usar el ámbito al referirse a una variable declarada globalmente, fuera de una función.
En este caso alert(window.x).
Hola David;
efectivamente, si la idea es referirse a la variable global, podemos acceder a ella a través del objeto window (contexto general del navegador).
Sin embargo, la idea aquí es no confundirse con el ámbito en el que se declaran las variables para así evitar comportamientos raros 🙂
Tú ejemplo quedaría:
Un saludo!
Bendito namespacing de todos modos…
function gracias(){
var etnassoft = «Carlos Benitez»;
i= 0;
for(i; i<5; i++){
alert("muchas gracias"+ " " + etnassoft);
}
}
gracias();
Esto esta bueno , aun pienso que mi codigo en js es un asco pero espero poco a poco tener un codigo minimalista pero funcional :B
Excelente explicación amigo, gracias 😀
Muchísimas gracias por tu explicación, todo muy claro y bien explicado.
Muy bien explicado, un saludo.
Muy buena la explicación, menos mal que en la facu nos enseñaron primero en funcional y luego en imperativo, evitándonos todos estos trastornos …
Gracias por la buena info 🙂
La solución a eso es usar la palabra reservada «this», para que digamos a interprete que vamos a usar la variable global.
alert( this.x ); // esperamos el valor global
var x = ‘New Value’; // redefinimos la variable en contexto local
alert( x ); // esperamos el nuevo valor local
}
foo();