Se hai iniziato a programmare con JavaScript prima del 2015, il tuo vocabolario per le variabili si limitava a una sola parola: var. Tuttavia, padroneggiare la triade var, let e const è oggi la competenza fondamentale per ogni sviluppatore moderno.
L’introduzione di ES6 (ECMAScript 2015) non ha portato solo “nuova sintassi”, ma ha risolto problemi architetturali storici del linguaggio. Comprendere le differenze non è solo una questione di stile; è la base per scrivere codice pulito (Clean Code), sicuro e performante.
In questa guida completa, analizzeremo non solo il come, ma il perché tecnico dietro Scope, Hoisting e la gestione della memoria.
Tabella Riassuntiva Rapida: Le Differenze
Prima di scendere nei dettagli tecnici, ecco la mappa mentale fondamentale:
| Caratteristica | var (Legacy) | let (Moderno) | const (Moderno) |
|---|---|---|---|
| Scope | Function Scope 🏠 | Block Scope 📦 | Block Scope 📦 |
| Riassegnabile? | ✅ Sì | ✅ Sì | ❌ No |
| Hoisting | Sì (inizializzato undefined) | Sì (in TDZ 💀) | Sì (in TDZ 💀) |
| Global Pollution | Sì (va su window) | No | No |
1. La Battaglia dello Scope: Dove vive la tua variabile?
Lo scope definisce l’area del codice in cui una variabile è “visibile”. Questa è la distinzione più critica e il motivo principale per cui var è stata quasi del tutto abbandonata.
1.1 var: Scope di Funzione (Il vecchio approccio)
Le variabili dichiarate con var ignorano i blocchi di codice come if o for. Esse “vedono” solo i confini della funzione in cui si trovano. Se non sono in una funzione, sono globali.
function usaVar() {
if (true) {
var messaggio = "Sono accessibile ovunque nella funzione!";
}
// Nessun errore: var scavalca il blocco if!
console.log(messaggio);
}1.2 let e const: Scope di Blocco (Il recinto sicuro)
Con let e const, le variabili nascono e muoiono all’interno delle parentesi graffe {} in cui sono dichiarate. Questo comportamento (Block Scope) previene fughe di dati involontarie.
function usaLet() {
if (true) {
let segreto = "Non puoi leggermi fuori";
}
// ❌ Errore: segreto is not defined
// console.log(segreto);
}1.3 Il Bug del Loop: Perché var ti tradisce nell’asincrono
Questa è la classica domanda da colloquio tecnico. La differenza di scope diventa critica quando usi callback asincrone (come setTimeout) dentro un ciclo.

Il problema con var:
Poiché var non ha scope di blocco, la variabile i è condivisa tra tutte le iterazioni. Quando il timer scatta (dopo 1 secondo), il ciclo è già finito e tutte le callback leggono lo stesso valore finale.
// ❌ Esempio errato con VAR
for (var i = 0; i < 3; i++) {
// 'i' è la stessa variabile per tutti!
setTimeout(() => console.log("Var: " + i), 1000);
}
// Output disastroso: "Var: 3", "Var: 3", "Var: 3"La soluzione con let:
Grazie allo scope di blocco, let crea una nuova associazione per la variabile in ogni singola iterazione. Ogni timer “ricorda” il valore esatto di quel giro.
// ✅ Esempio corretto con LET
for (let i = 0; i < 3; i++) {
// Una nuova 'i' viene creata per ogni iterazione
setTimeout(() => console.log("Let: " + i), 1000);
}
// Output corretto: "Let: 0", "Let: 1", "Let: 2"2. Hoisting e Temporal Dead Zone (TDZ)
L’Hoisting (sollevamento) è il comportamento del motore JavaScript che “sposta” le dichiarazioni in cima al loro scope. Ma c’è una sfumatura vitale da comprendere.
2.1 Var: Hoisting con inizializzazione
Con var, la variabile viene sollevata e inizializzata subito a undefined. Questo permetteva codice disordinato e buggato:
console.log(auto); // undefined (Nessun errore, ma dato sporco)
var auto = "Fiat";2.2 Let/Const: La Temporal Dead Zone
Molti pensano che let e const non subiscano hoisting. Falso. Vengono sollevate, ma non inizializzate.
Il periodo di tempo tra l’ingresso nello scope e la dichiarazione effettiva si chiama Temporal Dead Zone (TDZ). Se provi a toccare la variabile nella TDZ, JavaScript lancia un errore, proteggendoti.

{
// 💀 INIZIO TDZ
// console.log(piatto); // ReferenceError: Cannot access before initialization
let piatto = "Pasta"; // 🏁 FINE TDZ - La variabile ora è sicura
console.log(piatto); // "Pasta"
}3. Mutabilità e Gestione della Memoria
Quando usare let e quando const? La risposta sta nella mutabilità.
3.1 Const non significa “Immutabile al 100%”
const crea un legame (binding) immutabile. Una volta assegnato un valore a una costante, non puoi riassegnarla.
const TASSA = 22;
// TASSA = 25; // TypeError: Assignment to constant variable.3.2 Il trucco degli Oggetti (Reference vs Value)
Attenzione: const blocca l’assegnazione, ma non congela il valore se questo è un oggetto o un array. In termini di memoria: protegge l’indirizzo di casa, ma non ti impedisce di cambiare l’arredamento interno.

const utente = { nome: "Mario" };
// ❌ ERRORE: Non puoi cambiare il riferimento (l'indirizzo)
// utente = { nome: "Luigi" };
// ✅ OK: Puoi modificare il contenuto all'interno!
utente.nome = "Luigi";
console.log(utente.nome); // "Luigi"4. Best Practices per il 2025
Come scrivere codice pulito oggi? Segui questa semplice gerarchia:
- Default: const. Usa sempre const come prima scelta. Rende il codice più leggibile perché comunica che quel valore non dovrebbe cambiare.
- Opzione B: let. Usa let solo se sai per certo che il valore dovrà cambiare (es. contatori di loop, accumulatori).
- Mai: var. Non c’è quasi nessun motivo valido per usare var nel codice moderno, a meno che tu non stia lavorando su codice legacy molto datato.
Conclusione
Passare da var a let/const è stato uno dei più grandi salti di qualità nella storia di JavaScript. Hai guadagnato scope prevedibili, sicurezza contro gli errori di digitazione (grazie alla TDZ) e una gestione più chiara della memoria.
Sei pronto per il prossimo step? Ora che sai come dichiarare le variabili, scopri come estrarne i dati in modo elegante con la nostra guida sulla Destrutturazione in JavaScript (Prossimamente) o approfondisci come organizzare il tuo codice con i Moduli JavaScript.





