Var, let y const en JavaScript: El trío infernal explicado (con un toque de TypeScript)
¡Hola, desarrollador de JavaScript que te arrancas los pelos con estas variables! ¿Te preguntas cuándo usar var, let o const sin hacer el ridículo en las reuniones de equipo? Quédate, lo resolveremos juntos, con calma y una sonrisa. En el programa: exploraremos cada palabra clave, compararemos sus alcances (global, función, bloque), nos reiremos un poco del hoisting (ese mecanismo algo mágico de JavaScript), incluiremos ejemplos prácticos y terminaremos con un toque de TypeScript. ¿Listo? ¡Vamos!
La palabra clave var: El ancestro impredecible
Empecemos con var, el mayor de las variables de JavaScript. Si JavaScript fuera una serie de TV, var sería ese personaje de la vieja escuela, impredecible pero entrañable. Hasta ES5, era todo lo que teníamos para declarar nuestras variables. Todavía puedes usarlo hoy, pero cuidado: tiene algunas peculiaridades que pueden sorprenderte.
Características de var
- Alcance de var: Una variable declarada con var está limitada a la función que la contiene (o global si se declara fuera de cualquier función). Ignora las llaves de bloque (if, for, etc.). En resumen, un var declarado en un bloque sigue siendo accesible fuera del bloque, siempre que estemos en la misma función.
- Red declaración permitida: Con var, puedes declarar la misma variable varias veces en el mismo alcance sin error. Por ejemplo:
var x = 1; var x = 2;no desencadenará un error – JavaScript simplemente sobrescribirá el primer valor con el segundo. Útil para código legacy, pero una fuente de confusión que debe evitarse en código nuevo.
Para ilustrar el alcance excesivamente amplio de var, aquí hay un pequeño ejemplo:
javascript
Sí, aunque esta fruit fue declarada dentro de una declaración if, el console.log fuera del bloque la muestra sin problemas. A var no le importan los límites de bloque: es global o está unido a su función contenedora, punto.
Y lo mejor (o peor) para el final: el hoisting. En español a veces lo llamamos "elevación", pero seamos honestos, usamos principalmente el término hoisting. Es la tendencia de var a declararse silenciosamente al principio del alcance. JavaScript, durante la ejecución, actúa como si todas las declaraciones var hubieran sido "elevadas" al principio de la función. Como resultado, puedes usar tu variable antes de su línea de declaración... con un valor predeterminado algo extraño. Veamos esto más de cerca en la sección de hoisting más adelante (spoiler: var adora sorprenderte con undefined).
En resumen, var nos ha servido bien, pero es un poco traicionero en los bordes: alcance amplio, red declaración silenciosa, hoisting impredecible... Podemos hacerlo mejor, y afortunadamente, ES6 introdujo nuevas palabras clave para poner orden en el caos.
La palabra clave let: La nueva variable bien educada
En 2015, JavaScript dio la bienvenida a let (junto con const). Y ahí, obtuvimos un pequeño cambio de imagen de las variables. let es como el niño genial y disciplinado: corrige la mayoría de las peculiaridades de var mientras se mantiene flexible.
Características de let
- Alcance de let: Una variable declarada con let está limitada al bloque en el que está definida. Un bloque es cualquier cosa entre { ... } – ya sea una función, una declaración if, un bucle for, etc. Si declaras un let dentro de un bloque, no existe fuera. ¡Finalmente algo de orden! No más variables escapando de declaraciones if o bucles sin permiso.
- Sin red declaración: A diferencia de var, no puedes declarar la misma variable let dos veces en el mismo alcance. Si intentas hacer
let x = 1; let x = 2;en el mismo bloque, obtendrás un bonitoSyntaxError: Identifier 'x' has already been declared. Eso evita mucha confusión, admítelo.
Para ver claramente la diferencia de alcance con var, comparemos con el ejemplo anterior reemplazando var por let:
javascript
Con let, una vez que sales de las llaves de la declaración if, eso es todo, la variable vegetable ya no está definida. Intenta registrarla y bam: ReferenceError. Eso es let haciendo cumplir un buen comportamiento de alcance.
¿Y qué hay del hoisting? Las variables let (y const) también se elevan, pero de manera diferente. No son utilizables antes de ser declaradas. Técnicamente, el motor JS sabe que existen en el bloque (reserva la memoria), pero hasta que se ejecuta la línea de declaración, cualquier intento de acceso desencadenará un error. Decimos que estas variables están en la Zona Muerta Temporal (sí, suena como una película de terror, pero es solo el nombre elegante para "inaccesible antes de la inicialización"). Detallaremos este comportamiento más adelante, pero recuerda que let no te dejará usar una variable demasiado pronto.
En la práctica, let se ha convertido en la opción predeterminada para variables que cambiarán con el tiempo. Trae claridad (alcance de bloque) y seguridad (sin red declaración accidental, sin acceso antes de la declaración). En resumen, let es tu nuevo amigo para declarar variables temporales o modificables.
La palabra clave const: La apuesta segura (pero no congelada)
¡Ahora a const! Introducido al mismo tiempo que let, esta palabra clave crea constantes... o casi. Digamos que crea variables cuya referencia no cambiará. const tiene el mismo alcance de bloque que let (no más vars vagando) y las mismas reglas de hoisting (zona muerta temporal hasta la declaración). La gran diferencia es que una vez inicializada, la variable no puede ser reasignada.
Características de const
- Debe inicializarse: Con const, debes dar un valor en la declaración. No hay opción.
const x;solo no funcionará (SyntaxError directo). Necesitas hacerconst x = 42;por ejemplo. - Sin reasignación: Una vez que tu constante está definida, no hay forma de darle un nuevo valor más tarde. Si lo intentas, obtendrás un error en tiempo de ejecución (TypeError diciendo "assignment to constant variable"). El valor está fijo... bueno, la referencia está fija.
- Alcance de bloque y sin red declaración: Como let, un const vive en el bloque donde está declarado, y no puedes declarar el mismo nombre dos veces en el mismo alcance. Es limpio.
Mira este pequeño ejemplo para ver claramente la diferencia con una variable modificable:
javascript
Definimos PI como constante, podemos usarlo sin preocupaciones siempre que no intentemos cambiarlo. Cuando intentamos hacer PI = 3.15, JavaScript nos detiene en seco: no modificar una constante.
Nota: Constante no significa 100% inmutable. Si el valor es un objeto o un array, aún puedes modificar el interior del objeto/array. Lo que no se mueve es la variable misma (la referencia en memoria). Por ejemplo:
javascript
Aquí, pudimos cambiar name en el objeto user a pesar de const. Sin embargo, reasignar completamente user a otro objeto está prohibido. Entonces, const = constante, no modificable, pero solo a nivel de la variable misma. No confundas constancia de variable con inmutabilidad de valor.
javascript
Con un array, es la misma lógica: podemos modificar su contenido (añadir, eliminar, modificar elementos) porque solo estamos cambiando el interior del array. Pero no podemos reasignar la variable fruits a un nuevo array. Es como si el array fuera una caja: podemos cambiar lo que hay dentro, pero no podemos reemplazar la caja misma.
En la práctica, const es ideal para todos los valores que no deberían cambiar: por ejemplo, una configuración, una referencia, etc. Se usa ampliamente y hace que el código sea más robusto (sabemos que esta variable no se moverá). De hecho, las mejores prácticas a menudo recomiendan usar const por defecto, y cambiar a let solo cuando sabemos que el valor necesitará cambiar. En cuanto a var... bueno, a menos que haya una razón específica, lo olvidamos 😉.
Alcance de variables: ¿global, función o bloque?
Hablemos más sobre alcance. El alcance determina "dónde" una variable es accesible en tu código. Hay principalmente tres niveles de alcance en JavaScript:
Tipos de alcance
- Alcance global: Una variable es global cuando se declara fuera de cualquier función (en el script principal o la consola). Entonces es accesible en todas partes (en cualquier función o bloque). ⚠️ Advertencia, fuera de un módulo, un var global se convierte en una propiedad del objeto global (como window en un navegador), mientras que let y const crean variables globales "limpias" (no adjuntas a window). En un módulo ES6, las variables de nivel superior no se filtran al objeto global en absoluto.
- Alcance de función: Este es el alcance creado dentro de una función. Un var declarado en una función solo será visible dentro de esa función (y sus subfunciones si las hay). No podemos verlo fuera. Lo mismo para let y const declarados en una función: permanecen locales a esa función.
- Alcance de bloque: Este es el alcance limitado a un bloque delimitado por { ... } (por ejemplo dentro de una declaración if, un bucle for, un bucle while, o simplemente un bloque aislado). let y const tienen alcance de bloque, lo que significa que solo son accesibles en el bloque donde están definidos (y sus subbloques). var, por otro lado, no tiene alcance de bloque: su bloque de referencia es la función contenedora (o global si no hay función).
En resumen: var está limitado a la función (o global), mientras que let y const están limitados al bloque actual. Esta diferencia resuelve muchos problemas clásicos. Por ejemplo:
javascript
Aquí, el bucle con var deja i colgando fuera, con su valor final 3. El bucle con let, por otro lado, limpia j al salir: imposible acceder a él después. En la práctica, esto significa que puedes tener varias variables let con el mismo nombre en diferentes bloques sin interferencia, donde un solo var habría sido compartido entre todos estos bloques.
Hoisting: Cuando JavaScript juega al escondite
Pasemos al hoisting, este mecanismo de elevación de declaraciones. A menudo es una fuente de confusión (y bromas en reuniones), así que aclaremos. El hoisting es cuando el motor JavaScript "mueve" las declaraciones de variables y funciones al principio de su alcance durante la ejecución. En realidad, nada se mueve realmente en tu código, es solo que JavaScript asigna memoria para tus variables por adelantado, antes de ejecutar paso a paso.
Comportamiento del hoisting
- Para var: La declaración se eleva Y se inicializa a undefined por defecto. Así que puedes usar absolutamente una variable var antes de su línea de declaración, ya existe (con el valor undefined hasta que la asignación real llegue más tarde). Es un comportamiento legítimo del lenguaje, pero puede causar errores bastante insidiosos si no tienes cuidado.
- Para let y const: La declaración también se eleva (el motor sabe que hay una variable en este bloque), pero no se realiza ninguna inicialización por defecto. La variable se coloca en la Zona Muerta Temporal hasta que se ejecuta la línea de declaración. En la práctica, esto significa que si intentas leer o usar esta variable antes de su declaración real, JavaScript lanzará un error (ReferenceError). En otras palabras, la variable existe en teoría, pero no puedes acceder a ella hasta que se ejecute su declaración/inicialización.
Hagamos una pequeña prueba de hoisting en la práctica:
javascript
En el momento del primer console.log, la variable myVar se ha creado realmente en memoria a través del hoisting, pero su valor predeterminado es undefined (ya que aún no la hemos asignado). Sin error, solo se muestra undefined. Luego asignamos "Hola", y la segunda vez obtenemos el valor esperado.
Ahora veamos con let:
javascript
Aquí, el primer console.log desencadena un ReferenceError. ¿Por qué? Porque myLet se eleva sin inicialización. Está en su zona muerta temporal, inaccesible. JavaScript se niega a darnos un valor que aún no existe realmente. Una vez que ejecutamos let myLet = "Hola";, la variable sale de su zona muerta y obtiene "Hola". El segundo console.log funciona perfectamente.
Podemos verlo claramente: var se comporta como un ninja al elevar su declaración y darse silenciosamente el valor undefined, mientras que let/const juegan a lo seguro y previenen cualquier uso hasta que estén inicializados. Moraleja de la historia: evita codificar confiando en el hoisting, es una receta para nudos cerebrales (y undefineds inesperados). Mejor declarar tus variables al principio de sus alcances y mantener tu código claro. Si lo olvidas, el comportamiento de let/const te recordará con un error, donde var te habría dejado chapotear con un undefined.
Ah, y mientras estamos en ello, ten en cuenta que las funciones declaradas clásicamente (function myFunction() \{ ... \}) también se elevan completamente. Puedes llamar a una función declarada más abajo en tu código, JavaScript ya la conocerá. Pero ten cuidado, las funciones flecha o expresiones de función asignadas a variables siguen las reglas de hoisting de var/let según la palabra clave utilizada (un tema para otro día 😉).
Un rápido desvío por TypeScript: Aún más claridad
Antes de despedirnos, hablemos brevemente de TypeScript. Si empiezas a usar TypeScript (el superconjunto de JavaScript que añade tipado estático), encontrarás a nuestros tres amigos var, let y const. Buena noticia: sus comportamientos de alcance y hoisting permanecen exactamente igual que en JavaScript puro, ya que TypeScript se compila a JavaScript estándar. Sin embargo, TypeScript aporta su toque personal a la tipificación, y vale la pena echarle un vistazo, especialmente para let y const.
TypeScript y variables
- Inferencia de tipos: TypeScript adivina el tipo de tus variables basándose en el valor inicial. Si escribes
let fruit = "manzana";, TypeScript inferirá que fruit es de tipo string. Si escribeslet age = 25;, entenderá que age es un número, etc. Para let, se mantiene bastante amplio: la variable puede cambiar, así que el tipo es el básico (string, number, etc.). - Constantes y tipos literales: Donde se pone interesante es que con const, TypeScript inferirá un tipo literal. En otras palabras, como el valor es constante, TS puede permitirse decir "esta variable tiene exactamente este valor como su tipo". Por ejemplo:
typescript
La variable color es de tipo "rojo" y no solo string. ¿Beneficio? Si más tarde tu código espera exactamente el valor "rojo" o "azul" (por ejemplo un parámetro que solo puede ser uno de estos colores), y tienes const color = "rojo", puedes pasarlo sin preocupaciones. Con un let color = "rojo" (tipo string amplio), TypeScript se habría quejado porque "rojo" es solo una posibilidad entre todos los strings. En resumen, const en TypeScript permite una tipificación ultra precisa cuando es útil. Es como una bonificación que refuerza aún más el concepto de constante.
- Misma disciplina: TypeScript te animará a usar let y const exactamente como en ES6. De hecho, la mayoría de los proyectos TypeScript prohíben completamente var (incluso hay una regla de linter para eso). Y si intentas usar una variable antes de su declaración, TypeScript te gritará en tiempo de compilación – mucho antes de que lo haga el runtime de JavaScript. Por ejemplo, si escribes TypeScript con
console.log(myVar); var myVar = 3;, señalará que tienes un problema de alcance u orden. Lo mismo para un let usado demasiado pronto, TS conoce las reglas de hoisting y te protegerá (tanto como sea posible) de errores.
En resumen, TypeScript no cambia las reglas fundamentales de var/let/const, pero las refuerza con la red de seguridad de la tipificación. Ganas en claridad y atrapas errores antes. Recuerda especialmente que const en TypeScript es doblemente ganador: una variable no reasignable y un tipo literal preciso – ¿qué más podrías pedir?
Conclusión: Cuándo usar qué sin equivocarse
Hemos cubierto todo, así que ¿quién es el ganador en el partido var vs let vs const? Sin sorpresas: en 2025, el dúo let & const gana por goleada para escribir código limpio y mantenible. Aquí hay un resumen picante de consejos prácticos:
Mejores prácticas
- Usa const tanto como sea posible: Para cualquier variable cuyo valor no necesite cambiar, const es tu mejor amigo. Bloquea la reasignación y muestra claramente a todos (incluyéndote a ti en 6 meses) que este valor no se moverá. Perfecto para configuraciones, referencias fijas, etc.
- Si necesitas cambiar el valor, usa let: ¿Necesitas incrementar un contador, acumular un resultado, almacenar un valor intermedio modificable? let está ahí para eso. Se comporta bien (alcance de bloque, sin trampas inesperadas de hoisting), y te da la flexibilidad de cambiar el valor.
- Evita var excepto en casos extraños: Honestamente, var puede prácticamente desaparecer de tu vocabulario cotidiano. Sobrevive por razones de compatibilidad hacia atrás y algunos escenarios específicos, pero en código moderno ya no está realmente de moda. Si estás trabajando en un proyecto antiguo o necesitas manipular deliberadamente el alcance global (cosas que generalmente evitamos), vale, var podría mostrar su cara. De lo contrario, puedes ponerlo en el museo de reliquias de JavaScript 😄.
- En caso de duda, explícate: Si algún día usas var y alguien pregunta por qué en una reunión, ten una buena razón y explícala (por ejemplo, "Necesito declarar esta variable globalmente para que los scripts antiguos puedan acceder a ella"). Pero el 99% del tiempo, no necesitarás esta gimnasia: let y const cubrirán todas tus necesidades sin pestañear.
Ahí lo tienes, ahora sabes cuándo usar var, let o const en JavaScript, e incluso un poco sobre cómo funciona con TypeScript. No más confusión sobre nuestros tres compañeros ni burlas en la revisión de código porque sacaste un var de la manga sin una razón válida. Ve a codificar pacíficamente, y no olvides: const tanto como sea posible, let si es necesario, y var... ¡lo menos posible! ¡Feliz codificación!