Var, let e const in JavaScript: Il trio infernale spiegato (con un tocco di TypeScript)
Ciao, sviluppatore JavaScript che ti strappi i capelli con queste variabili! Ti chiedi quando usare var, let o const senza fare figure barbine nelle riunioni di team? Resta con me, lo risolveremo insieme, con calma e un sorriso. Nel programma: esploreremo ogni parola chiave, confronteremo i loro scope (globale, funzione, blocco), rideremo un po' dell'hoisting (quel meccanismo un po' magico di JavaScript), includeremo esempi pratici e finiremo con un tocco di TypeScript. Pronto? Andiamo!
La parola chiave var: L'antenato imprevedibile
Iniziamo con var, il più anziano delle variabili JavaScript. Se JavaScript fosse una serie TV, var sarebbe quel personaggio old-school, imprevedibile ma affascinante. Fino a ES5, era tutto ciò che avevamo per dichiarare le nostre variabili. Puoi ancora usarlo oggi, ma attenzione: ha alcune peculiarità che potrebbero sorprenderti.
Caratteristiche di var
- Scope di var: Una variabile dichiarata con var è limitata alla funzione che la contiene (o globale se dichiarata fuori da qualsiasi funzione). Ignora le parentesi graffe del blocco (if, for, ecc.). In breve, un var dichiarato in un blocco rimane accessibile fuori dal blocco, purché siamo nella stessa funzione.
- Rideclarazione permessa: Con var, puoi dichiarare la stessa variabile più volte nello stesso scope senza errore. Per esempio:
var x = 1; var x = 2;non genererà un errore – JavaScript semplicemente sovrascriverà il primo valore con il secondo. Utile per il codice legacy, ma una fonte di confusione da evitare nel nuovo codice.
Per illustrare lo scope eccessivamente ampio di var, ecco un piccolo esempio:
javascript
Sì, anche se questo fruit è stato dichiarato all'interno di un'istruzione if, il console.log fuori dal blocco lo mostra senza problemi. A var non importano i limiti del blocco: è globale o legato alla sua funzione contenitore, punto.
E il meglio (o il peggio) per ultimo: l'hoisting. In italiano a volte lo chiamiamo "sollevamento", ma siamo onesti, usiamo principalmente il termine hoisting. È la tendenza di var a dichiararsi silenziosamente all'inizio dello scope. JavaScript, durante l'esecuzione, si comporta come se tutte le dichiarazioni var fossero state "sollevate" all'inizio della funzione. Di conseguenza, puoi usare la tua variabile prima della sua linea di dichiarazione... con un valore predefinito un po' strano. Vediamo questo più da vicino nella sezione hoisting più avanti (spoiler: var adora sorprenderti con undefined).
In sintesi, var ci ha servito bene, ma è un po' subdolo ai bordi: scope ampio, rideclarazione silenziosa, hoisting imprevedibile... Possiamo fare meglio, e fortunatamente, ES6 ha introdotto nuove parole chiave per mettere ordine nel caos.
La parola chiave let: La nuova variabile ben educata
Nel 2015, JavaScript ha accolto let (insieme a const). E lì, abbiamo ottenuto un piccolo restyling delle variabili. let è come il ragazzo figo e disciplinato: corregge la maggior parte delle peculiarità di var mantenendosi flessibile.
Caratteristiche di let
- Scope di let: Una variabile dichiarata con let è limitata al blocco in cui è definita. Un blocco è qualsiasi cosa tra { ... } – sia una funzione, un'istruzione if, un ciclo for, ecc. Se dichiari un let all'interno di un blocco, non esiste fuori. Finalmente un po' di ordine! Non più variabili che scappano da istruzioni if o cicli senza permesso.
- Nessuna rideclarazione: A differenza di var, non puoi dichiarare la stessa variabile let due volte nello stesso scope. Se provi a fare
let x = 1; let x = 2;nello stesso blocco, otterrai un belSyntaxError: Identifier 'x' has already been declared. Questo evita molta confusione, ammettilo.
Per vedere chiaramente la differenza di scope con var, confrontiamo con l'esempio precedente sostituendo var con let:
javascript
Con let, una volta usciti dalle parentesi graffe dell'istruzione if, è fatta, la variabile vegetable non è più definita. Prova a registrarla e bam: ReferenceError. Questo è let che fa rispettare un buon comportamento di scope.
E l'hoisting? Le variabili let (e const) vengono anche sollevate, ma in modo diverso. Non sono utilizzabili prima di essere dichiarate. Tecnicamente, il motore JS sa che esistono nel blocco (riserva la memoria), ma finché non viene eseguita la linea di dichiarazione, qualsiasi tentativo di accesso genererà un errore. Diciamo che queste variabili sono nella Zona Morta Temporale (sì, suona come un film horror, ma è solo il nome elegante per "inaccessibile prima dell'inizializzazione"). Dettaglieremo questo comportamento più avanti, ma ricorda che let non ti lascerà usare una variabile troppo presto.
Nella pratica, let è diventata l'opzione predefinita per le variabili che cambieranno nel tempo. Porta chiarezza (scope di blocco) e sicurezza (nessuna rideclarazione accidentale, nessun accesso prima della dichiarazione). In breve, let è il tuo nuovo amico per dichiarare variabili temporanee o modificabili.
La parola chiave const: La scommessa sicura (ma non congelata)
Ora a const! Introdotto contemporaneamente a let, questa parola chiave crea costanti... o quasi. Diciamo che crea variabili la cui riferimento non cambierà. const ha lo stesso scope di blocco di let (non più var che vagano) e le stesse regole di hoisting (zona morta temporale fino alla dichiarazione). La grande differenza è che una volta inizializzata, la variabile non può essere riassegnata.
Caratteristiche di const
- Deve essere inizializzata: Con const, devi dare un valore nella dichiarazione. Non c'è scelta.
const x;da solo non funzionerà (SyntaxError diretto). Devi fareconst x = 42;per esempio. - Nessuna riassegnazione: Una volta che la tua costante è definita, non c'è modo di darle un nuovo valore più tardi. Se ci provi, otterrai un errore in fase di esecuzione (TypeError dicendo "assignment to constant variable"). Il valore è fisso... beh, il riferimento è fisso.
- Scope di blocco e nessuna rideclarazione: Come let, un const vive nel blocco dove è dichiarato, e non puoi dichiarare lo stesso nome due volte nello stesso scope. È pulito.
Guarda questo piccolo esempio per vedere chiaramente la differenza con una variabile modificabile:
javascript
Definiamo PI come costante, possiamo usarlo senza preoccupazioni finché non proviamo a cambiarlo. Quando proviamo a fare PI = 3.15, JavaScript ci ferma di colpo: non modificare una costante.
Nota: Costante non significa 100% immutabile. Se il valore è un oggetto o un array, puoi ancora modificare l'interno dell'oggetto/array. Quello che non si muove è la variabile stessa (il riferimento in memoria). Per esempio:
javascript
Qui, abbiamo potuto cambiare name nell'oggetto user nonostante const. Tuttavia, riassegnare completamente user a un altro oggetto è proibito. Quindi, const = costante, non modificabile, ma solo a livello della variabile stessa. Non confondere costanza di variabile con immutabilità di valore.
javascript
Con un array, è la stessa logica: possiamo modificare il suo contenuto (aggiungere, eliminare, modificare elementi) perché stiamo solo cambiando l'interno dell'array. Ma non possiamo riassegnare la variabile fruits a un nuovo array. È come se l'array fosse una scatola: possiamo cambiare quello che c'è dentro, ma non possiamo sostituire la scatola stessa.
Nella pratica, const è ideale per tutti i valori che non dovrebbero cambiare: per esempio una configurazione, un riferimento, ecc. È ampiamente usato e rende il codice più robusto (sappiamo che questa variabile non si muoverà). In effetti, le best practice spesso raccomandano di usare const di default, e passare a let solo quando sappiamo che il valore dovrà cambiare. Quanto a var... beh, a meno che non ci sia una ragione specifica, lo dimentichiamo 😉.
Scope delle variabili: globale, funzione o blocco?
Parliamo di più di scope. Lo scope determina "dove" una variabile è accessibile nel tuo codice. Ci sono principalmente tre livelli di scope in JavaScript:
Tipi di scope
- Scope globale: Una variabile è globale quando è dichiarata fuori da qualsiasi funzione (nello script principale o nella console). È quindi accessibile ovunque (in qualsiasi funzione o blocco). ⚠️ Attenzione, fuori da un modulo, un var globale diventa una proprietà dell'oggetto globale (come window in un browser), mentre let e const creano variabili globali "pulite" (non attaccate a window). In un modulo ES6, le variabili di livello superiore non fuoriescono affatto nell'oggetto globale.
- Scope di funzione: Questo è lo scope creato all'interno di una funzione. Un var dichiarato in una funzione sarà visibile solo all'interno di quella funzione (e delle sue sottofunzioni se presenti). Non possiamo vederlo fuori. Lo stesso vale per let e const dichiarati in una funzione: rimangono locali a quella funzione.
- Scope di blocco: Questo è lo scope limitato a un blocco delimitato da { ... } (per esempio all'interno di un'istruzione if, un ciclo for, un ciclo while, o semplicemente un blocco isolato). let e const hanno scope di blocco, il che significa che sono accessibili solo nel blocco dove sono definiti (e i loro sottoblocchi). var, d'altra parte, non ha scope di blocco: il suo blocco di riferimento è la funzione contenitore (o globale se non c'è funzione).
In sintesi: var è limitato alla funzione (o globale), mentre let e const sono limitati al blocco corrente. Questa differenza risolve molti problemi classici. Per esempio:
javascript
Qui, il ciclo con var lascia i appeso fuori, con il suo valore finale 3. Il ciclo con let, d'altra parte, pulisce j all'uscita: impossibile accedervi dopo. Nella pratica, questo significa che puoi avere diverse variabili let con lo stesso nome in blocchi diversi senza interferenza, dove un singolo var sarebbe stato condiviso tra tutti questi blocchi.
Hoisting: Quando JavaScript gioca a nascondino
Passiamo all'hoisting, questo meccanismo di sollevamento delle dichiarazioni. Spesso è una fonte di confusione (e battute nelle riunioni), quindi chiariamolo. L'hoisting è quando il motore JavaScript "sposta" le dichiarazioni di variabili e funzioni all'inizio del loro scope durante l'esecuzione. In realtà, nulla si muove davvero nel tuo codice, è solo che JavaScript alloca memoria per le tue variabili in anticipo, prima di eseguire passo dopo passo.
Comportamento dell'hoisting
- Per var: La dichiarazione viene sollevata E inizializzata a undefined di default. Quindi puoi assolutamente usare una variabile var prima della sua linea di dichiarazione, esiste già (con il valore undefined finché l'assegnazione reale non arriva più tardi). È un comportamento legittimo del linguaggio, ma può causare bug piuttosto insidiosi se non stai attento.
- Per let e const: La dichiarazione viene anche sollevata (il motore sa che c'è una variabile in questo blocco), ma non viene eseguita alcuna inizializzazione di default. La variabile viene posta nella Zona Morta Temporale finché non viene eseguita la linea di dichiarazione. Nella pratica, questo significa che se provi a leggere o usare questa variabile prima della sua dichiarazione effettiva, JavaScript lancerà un errore (ReferenceError). In altre parole, la variabile esiste in teoria, ma non puoi accedervi finché non viene eseguita la sua dichiarazione/inizializzazione.
Facciamo un piccolo test di hoisting nella pratica:
javascript
Al momento del primo console.log, la variabile myVar è stata effettivamente creata in memoria attraverso l'hoisting, ma il suo valore predefinito è undefined (poiché non l'abbiamo ancora assegnata). Nessun errore, viene mostrato solo undefined. Poi assegniamo "Ciao", e la seconda volta otteniamo il valore atteso.
Ora vediamo con let:
javascript
Qui, il primo console.log scatena un ReferenceError. Perché? Perché myLet viene sollevato senza inizializzazione. È nella sua zona morta temporale, inaccessibile. JavaScript si rifiuta di darci un valore che non esiste ancora realmente. Una volta che eseguiamo let myLet = "Ciao";, la variabile esce dalla sua zona morta e ottiene "Ciao". Il secondo console.log funziona perfettamente.
Possiamo vederlo chiaramente: var si comporta come un ninja sollevando la sua dichiarazione e dandosi silenziosamente il valore undefined, mentre let/const giocano sul sicuro e prevengono qualsiasi uso finché non sono inizializzati. Morale della storia: evita di codificare affidandoti all'hoisting, è una ricetta per nodi cerebrali (e undefined inaspettati). Meglio dichiarare le tue variabili all'inizio dei loro scope e mantenere il tuo codice chiaro. Se lo dimentichi, il comportamento di let/const ti ricorderà con un errore, dove var ti avrebbe lasciato sguazzare con un undefined.
Ah, e mentre ci siamo, nota che le funzioni dichiarate classicamente (function myFunction() \{ ... \}) vengono anche sollevate completamente. Puoi chiamare una funzione dichiarata più in basso nel tuo codice, JavaScript la conoscerà già. Ma attenzione, le funzioni freccia o espressioni di funzione assegnate a variabili seguono le regole di hoisting di var/let secondo la parola chiave utilizzata (un argomento per un altro giorno 😉).
Una rapida deviazione in TypeScript: Ancora più chiarezza
Prima di salutarci, parliamo brevemente di TypeScript. Se inizi a usare TypeScript (il super-set di JavaScript che aggiunge il tipaggio statico), troverai i nostri tre amici var, let e const. Buone notizie: i loro comportamenti di scope e hoisting rimangono esattamente gli stessi che in JavaScript puro, poiché TypeScript viene compilato in JavaScript standard. Tuttavia, TypeScript porta il suo tocco personale alla tipizzazione, e vale la pena darci un'occhiata, specialmente per let e const.
TypeScript e variabili
- Inferenza di tipo: TypeScript indovina il tipo delle tue variabili basandosi sul valore iniziale. Se scrivi
let fruit = "mela";, TypeScript inferirà che fruit è di tipo string. Se scrivilet age = 25;, capirà che age è un numero, ecc. Per let, rimane abbastanza ampio: la variabile può cambiare, quindi il tipo è quello base (string, number, ecc.). - Costanti e tipi letterali: Dove diventa interessante è che con const, TypeScript inferirà un tipo letterale. In altre parole, poiché il valore è costante, TS può permettersi di dire "questa variabile ha esattamente questo valore come suo tipo". Per esempio:
typescript
La variabile color è di tipo "rosso" e non solo string. Vantaggio? Se più tardi il tuo codice si aspetta esattamente il valore "rosso" o "blu" (per esempio un parametro che può essere solo uno di questi colori), e hai const color = "rosso", puoi passarlo senza preoccupazioni. Con un let color = "rosso" (tipo string ampio), TypeScript si sarebbe lamentato perché "rosso" è solo una possibilità tra tutte le stringhe. In breve, const in TypeScript permette una tipizzazione ultra precisa quando è utile. È come un bonus che rafforza ulteriormente il concetto di costante.
- Stessa disciplina: TypeScript ti incoraggerà a usare let e const esattamente come in ES6. In effetti, la maggior parte dei progetti TypeScript vieta completamente var (c'è persino una regola del linter per questo). E se provi a usare una variabile prima della sua dichiarazione, TypeScript ti griderà in fase di compilazione – molto prima che lo faccia il runtime di JavaScript. Per esempio, se scrivi TypeScript con
console.log(myVar); var myVar = 3;, segnalerà che hai un problema di scope o ordine. Lo stesso per un let usato troppo presto, TS conosce le regole di hoisting e ti proteggerà (per quanto possibile) dagli errori.
In sintesi, TypeScript non cambia le regole fondamentali di var/let/const, ma le rafforza con la rete di sicurezza della tipizzazione. Guadagni in chiarezza e catturi gli errori prima. Ricorda specialmente che const in TypeScript è doppiamente vincente: una variabile non riassegnabile e un tipo letterale preciso – cosa potresti volere di più?
Conclusione: Quando usare cosa senza sbagliare
Abbiamo coperto tutto, quindi chi è il vincitore nella partita var vs let vs const? Nessuna sorpresa: nel 2025, il duo let & const vince a mani basse per scrivere codice pulito e manutenibile. Ecco un riassunto piccante di consigli pratici:
Best practices
- Usa const il più possibile: Per qualsiasi variabile il cui valore non debba cambiare, const è il tuo migliore amico. Blocca la riassegnazione e mostra chiaramente a tutti (incluso te tra 6 mesi) che questo valore non si muoverà. Perfetto per configurazioni, riferimenti fissi, ecc.
- Se devi cambiare il valore, usa let: Hai bisogno di incrementare un contatore, accumulare un risultato, memorizzare un valore intermedio modificabile? let è lì per questo. Si comporta bene (scope di blocco, nessuna trappola inaspettata di hoisting), e ti dà la flessibilità di cambiare il valore.
- Evita var tranne in casi strani: Onestamente, var può praticamente scomparire dal tuo vocabolario quotidiano. Sopravvive per ragioni di compatibilità all'indietro e alcuni scenari specifici, ma nel codice moderno non è più davvero di moda. Se stai lavorando su un progetto vecchio o devi manipolare deliberatamente lo scope globale (cose che generalmente evitiamo), ok, var potrebbe mostrare la sua faccia. Altrimenti, puoi metterlo nel museo delle reliquie di JavaScript 😄.
- In caso di dubbio, spiegati: Se un giorno usi var e qualcuno chiede perché in una riunione, abbi una buona ragione e spiegala (per esempio, "Devo dichiarare questa variabile globalmente perché gli script vecchi possano accedervi"). Ma il 99% delle volte, non avrai bisogno di questa ginnastica: let e const copriranno tutti i tuoi bisogni senza battere ciglio.
Ecco fatto, ora sai quando usare var, let o const in JavaScript, e anche un po' su come funziona con TypeScript. Non più confusione sui nostri tre compagni né prese in giro nella code review perché hai tirato fuori un var dal cappello senza una ragione valida. Vai a codificare pacificamente, e non dimenticare: const il più possibile, let se necessario, e var... il meno possibile! Happy coding!