preloader

La antigua «var» en JavaScript

La antigua «var»

Este artículo es útil para entender código antiguo. La información aquí es relevante para interpretar y trabajar con código más viejo.

No es así como escribimos código moderno.

En el primer capítulo sobre variables, mencionamos tres formas de declarar una variable:

  • let
  • const
  • var

La declaración var es similar a let. Casi siempre podemos reemplazar let por var o viceversa y esperar que el código funcione:

				
					var mensaje = "Hola";
alert(mensaje); // Hola

				
			

Sin embargo, internamente var es diferente, originario de tiempos muy antiguos. Generalmente no se usa en código moderno, pero aún aparece en código viejo.

Si no planeas encontrarte con tal código, puedes saltar este capítulo o posponerlo, pero hay posibilidades de que esta diferencia pueda afectarte más tarde.

Por otro lado, es importante entender las diferencias cuando se migra código antiguo de var a let para evitar errores extraños.

var no tiene alcance de bloque

Las variables declaradas con var pueden tener a la función como entorno de visibilidad o ser globales. Su visibilidad atraviesa los bloques.

Por ejemplo:

				
					if (true) {
  var test = true; // uso de "var" en lugar de "let"
}

alert(test); // true, la variable vive después del if

				
			

Como var ignora los bloques de código, tenemos una variable global test.

Si usáramos let test en vez de var test, la variable sería visible solamente dentro del if:

				
					if (true) {
  let test = true; // uso de "let"
}

alert(test); // ReferenceError: test no está definido

				
			

Lo mismo sucede en los bucles: var no puede ser local en los bloques ni en los bucles:

				
					for (var i = 0; i < 10; i++) {
  var uno = 1;
  // ...
}

alert(i); // 10, "i" es visible después del bucle, es una variable global
alert(uno); // 1, "uno" es visible después del bucle, es una variable global

				
			

Si un bloque de código está dentro de una función, var se vuelve una variable a nivel de función:

				
					function decirHola() {
  if (true) {
    var frase = "Hola";
  }

  alert(frase); // funciona
}

decirHola();
alert(frase); // ReferenceError: frase no está definida

				
			

Como podemos ver, var atraviesa if, for u otros bloques. Esto es porque mucho tiempo atrás los bloques en JavaScript no tenían entornos léxicos, y var es un remanente de aquello.

var tolera redeclaraciones

Declarar la misma variable con let dos veces en el mismo entorno es un error:

				
					let usuario;
let usuario; // SyntaxError: 'usuario' ya fue declarado

				
			

Con var podemos redeclarar una variable muchas veces. Si usamos var con una variable ya declarada, simplemente se ignora:

				
					var usuario = "Pedro";

var usuario = "Juan"; // este "var" no hace nada (ya estaba declarado)
// ...no dispara ningún error

alert(usuario); // Juan

				
			

Las variables var pueden ser declaradas debajo del lugar donde se usan

Las declaraciones var son procesadas cuando se inicia la función (o se inicia el script para las globales).

En otras palabras, las variables var son definidas desde el inicio de la función, sin importar dónde esté tal definición (asumiendo que la definición no está en una función anidada).

Entonces el código:

				
					function decirHola() {
  frase = "Hola";

  alert(frase);

  var frase;
}
decirHola();

				
			

…es técnicamente lo mismo que esto (se movió var frase hacia arriba):

				
					function decirHola() {
  frase = "Hola"; // (*)

  if (false) {
    var frase;
  }

  alert(frase);
}
decirHola();

				
			

Este comportamiento también se llama “hoisting” (elevación), porque todos los var son “elevados” hacia el tope de la función.

Entonces, en el ejemplo anterior, la rama if (false) nunca se ejecuta, pero eso no tiene importancia. El var dentro es procesado al iniciar la función, entonces al momento de (*) la variable existe.

Las declaraciones son “hoisted” (elevadas), pero las asignaciones no lo son

Es mejor demostrarlo con un ejemplo:

				
					function decirHola() {
  alert(frase);

  var frase = "Hola";
}

decirHola();

				
			

La línea var frase = "Hola" tiene dentro dos acciones:

  1. La declaración var
  2. La asignación =.

La declaración es procesada al inicio de la ejecución de la función (“elevada”), pero la asignación siempre se hace en el lugar donde aparece. Entonces lo que en esencia hace el código es:

				
					function decirHola() {
  var frase; // la declaración se hace al inicio...

  alert(frase); // undefined

  frase = "Hola"; // ...asignación - cuando la ejecución la alcanza.
}

decirHola();

				
			

Como todas las declaraciones var son procesadas al inicio de la función, podemos referenciarlas en cualquier lugar. Pero las variables serán indefinidas hasta que alcancen su asignación.

En ambos ejemplos de arriba, alert se ejecuta sin un error, porque la variable frase existe. Pero su valor no fue asignado aún, entonces muestra undefined.

IIFE

Como en el pasado solo existía var, y no había visibilidad a nivel de bloque, los programadores inventaron una manera de emularla. Lo que hicieron fue usar «expresiones de función inmediatamente invocadas» (abreviado IIFE en inglés).

No es algo que debiéramos usar estos días, pero puedes encontrarlas en código antiguo.

Un IIFE se ve así:

				
					(function() {

  var mensaje = "Hola";

  alert(mensaje); // Hola

})();

				
			

Aquí la expresión de función es creada e inmediatamente llamada. Entonces el código se ejecuta enseguida y con sus propias variables privadas.

La expresión de función es encerrada entre paréntesis (function {...}), porque cuando JavaScript se encuentra con «function» en el flujo de código principal, lo entiende como el principio de una declaración de función. Pero una declaración de función debe tener un nombre, entonces ese código daría error:

				
					// Trata de declarar e inmediatamente llamar una función
function() { // <-- SyntaxError: la instrucción de función requiere un nombre de función

  var mensaje = "Hola";

  alert(mensaje); // Hola

}();

				
			

Incluso si decimos: “okay, agreguemos un nombre”, no funcionaría, porque JavaScript no permite que las declaraciones de función sean llamadas inmediatamente:

				
					// error de sintaxis por causa de los paréntesis debajo
function ir() {

}(); // <-- no puede llamarse una declaración de función inmediatamente

				
			

Entonces, los paréntesis alrededor de la función son un truco para mostrarle a JavaScript que la función es creada en el contexto de otra expresión, y de allí lo de “expresión de función”, que no necesita un nombre y puede ser llamada inmediatamente.

Existen otras maneras además de los paréntesis para decirle a JavaScript que queremos una expresión de función:

				
					// Formas de crear IIFE

(function() {
  alert("Paréntesis alrededor de la función");
})();

(function() {
  alert("Paréntesis alrededor de todo");
}());

!function() {
  alert("Operador 'Bitwise NOT' como comienzo de la expresión");
}();

+function() {
  alert("'más unario' como comienzo de la expresión");
}();

				
			

En todos los casos de arriba, declaramos una expresión de función y la ejecutamos inmediatamente. Tomemos nota de nuevo: Ahora no hay motivo para escribir semejante código.

Resumen

Hay dos diferencias principales entre var y let/const:

  1. Las variables var no tienen alcance de bloque: su visibilidad alcanza a la función, o es global si es declarada fuera de las funciones.
  2. Las declaraciones var son procesadas al inicio de la función (o del script para las globales).

Estas diferencias casi siempre hacen a var peor que let. Las variables a nivel de bloque son mejores. Es por ello que let fue introducido en el estándar hace mucho tiempo, y es ahora la forma principal (junto con const) de declarar una variable.

Related Post

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *