let, var, const — Diferencias en la declaración de variables
Al finalizar este tema
Podrás explicar las diferencias entre var, let y const desde las perspectivas del alcance (scope) y la reasignación, y podrás determinar qué utilizar en cada situación.
Las tres palabras clave de declaración
En JavaScript, existen tres formas de declarar variables:
var name = "Cheolsu"; // ES5 (estilo antiguo)
let age = 25; // ES6 (2015~)
const PI = 3.14159; // ES6 (2015~)En todos los casos, se "crea un nombre que almacena un valor". La diferencia radica en el alcance (hasta dónde es visible) y la reasignación (si se puede cambiar el valor).
Posibilidad de reasignación
let count = 0;
count = 1; // ✅ let permite reasignación
const MAX = 100;
MAX = 200; // ❌ TypeError: Assignment to constant variableconst no puede ser reasignado después de su declaración; una vez asignado, el valor no se puede cambiar. let y var sí permiten la reasignación.
Nota: const significa que "el enlace de la variable es inmutable", no que "el valor sea inmutable". El contenido interno de objetos o arreglos puede modificarse:
const user = { name: "Cheolsu" };
user.name = "Younghee"; // ✅ Es posible modificar el interior del objeto
user = {}; // ❌ No es posible cambiar la variable en sí por otro valorÁmbito — var frente a let/const
var tiene un ámbito de función. Ignora los bloques de las instrucciones if o for ({}):
function example() {
if (true) {
var x = 10;
}
console.log(x); // 10 — ¡Es posible acceder fuera del bloque if!
}let/const tienen alcance de bloque. Solo existen dentro del bloque declarado ({}):
function example() {
if (true) {
let y = 10;
}
console.log(y); // ReferenceError: y is not defined
}El alcance de bloque es intuitivo: solo existe dentro de las llaves donde se declara y desaparece fuera de ellas.
Diferencias en el bucle for
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Salida: 3, 3, 3 — Todos hacen referencia al mismo iLa variable declarada con var, i, ignora el bloque del bucle for; por lo tanto, cuando se ejecuta setTimeout, i ya ha alcanzado el valor 3.
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Salida: 0, 1, 2 — Cada iteración crea un nuevo ilet crea un nuevo bloque de ámbito en cada iteración, por lo que cada i existe de forma independiente. Esta es la diferencia más importante entre let y var.
Elevación (Hoisting)
La elevación (hoisting) es el fenómeno mediante el cual las declaraciones se mueven al inicio del código.
console.log(a); // undefined (¡No es un error!)
var a = 5;
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 5;var se inicializa como undefined debido al hoisting de la declaración. Por lo tanto, acceder antes de la declaración no genera un error, lo que dificulta encontrar errores.
let y const también se someten a hoisting, pero generan un error si se accede a ellos antes de su inicialización. Este intervalo se denomina ZDT (Temporal Dead Zone). Es más ventajoso para la depuración que aparezcan errores.
Guías prácticas
// 1. El valor predeterminado es const
const API_URL = "https://api.example.com";
const users = [];
// 2. let solo cuando el valor deba cambiar
let count = 0;
count++;
// 3. No uses var
// (Solo visible en código legado)| Palabra clave | Ámbito | Reasignación | Elevación (Hoisting) | Momento de uso |
|---|---|---|---|---|
| var | Función | ✅ | Se inicializa como undefined | No usar |
| let | Bloque | ✅ | TDZ (error) | Cuando el valor cambia |
| const | Bloque | ❌ | TDZ (error) | Valor por defecto |
En JavaScript moderno, se debe usar const como valor predeterminado y cambiar a let solo cuando sea necesaria la reasignación. var permanece únicamente por razones históricas; no hay razón para usarlo en código nuevo.
El famoso error de las clausuras con var
El error más conocido que crea el ámbito de función de var:
// Quiero mostrar números diferentes para cada uno de los 5 botones
for (var i = 0; i < 5; i++) {
document.getElementById("btn" + i).onclick = function() {
alert(i); // alerta 5 en todos los casos
};
}Todos los botones muestran 5. En el momento del clic, i ya es 5 porque todos los callbacks hacen referencia al mismo i.
// Se soluciona usando let
for (let i = 0; i < 5; i++) {
document.getElementById("btn" + i).onclick = function() {
alert(i); // 0, 1, 2, 3, 4
};
}Como let crea un nuevo ámbito en cada iteración, cada devolución de llamada tiene su propia i. Antes de ES6, se utilizaba una IIFE (expresión de función ejecutada inmediatamente) como solución alternativa; con la introducción de let, este patrón ya no es necesario.
var/let/const en declaraciones de funciones
También existen diferencias dependiendo del método de declaración al crear funciones:
// Declaración de función — se eleva (hoisting)
greet(); // ✅ Funciona
function greet() {
console.log("Hola");
}
// Expresión de función con const — no se eleva (no hoisting)
hello(); // ❌ ReferenceError
const hello = () => {
console.log("Hola");
};Las declaraciones de funciones pueden invocarse en cualquier parte del código, pero las funciones declaradas con const solo pueden invocarse después de su declaración. Por lo general, los proyectos unifican el uso de uno de estos enfoques.
Diferencias en el ámbito global
// En el entorno del navegador
var globalVar = "soy var";
let globalLet = "soy let";
console.log(window.globalVar); // "soy var" — se añade al objeto window
console.log(window.globalLet); // undefined — no se añade a windowLas variables globales declaradas con var se convierten en propiedades del objeto window. Esto puede causar conflictos de nombres entre bibliotecas. Las declaraciones de let y const en el ámbito global no se agregan a window.
Errores comunes y soluciones
// Error 1: modificar el interior de un objeto const es legal
const config = { debug: true };
config.debug = false; // ✅ permitido — modificación del interior del objeto
config = {}; // ❌ no permitido — reasignación de la variable
// Error 2: usar const en un bucle for
for (const i = 0; i < 3; i++) { // ❌ error de reasignación en i++
console.log(i);
}
// En for...of se puede usar const (nueva vinculación en cada iteración)
for (const item of [1, 2, 3]) { // ✅
console.log(item);
}Object.freeze: crear inmutabilidad real
const Para impedir realmente la modificación del interior de un objeto:
const config = Object.freeze({
apiUrl: "https://api.example.com",
maxRetries: 3
});
config.apiUrl = "changed"; // ignorado silenciosamente (error en modo estricto)
console.log(config.apiUrl); // "https://api.example.com"Object.freeze() impide la adición, modificación o eliminación de propiedades del objeto. Sin embargo, al ser una congelación superficial (shallow freeze), el contenido interno de los objetos anidados sigue siendo modificable.
Al leer código, si se ve const, se puede saber inmediatamente que "este valor no cambiará". Esta previsibilidad se vuelve cada vez más importante a medida que el proyecto crece.