
"Cannot access before initialization": la Temporal Dead Zone spiegata
Perché let e const lanciano ReferenceError invece di restituire undefined come var, cos'è davvero la Temporal Dead Zone, e i pattern che la scatenano per errore.
Fai riferimento a una variabile chiaramente dichiarata più sotto nello stesso scope, aspettandoti la solita elasticità di JavaScript, e invece ottieni:
⛔ Errore
ReferenceError: Cannot access ‘config’ before initialization
Se vieni da var, o da un linguaggio senza questa regola esatta, è sorprendente — var ti avrebbe semplicemente dato undefined invece di lanciare un errore. La differenza è la Temporal Dead Zone, e una volta capita, questo errore smette di essere confuso.
Hoisting di var vs hoisting di let/const
Tutti e tre — var, let e const — sono soggetti a hoisting: il motore conosce l’esistenza della variabile fin dall’inizio del suo scope. La differenza è cosa succede tra l’inizio dello scope e la riga di dichiarazione effettiva.
console.log(x); // undefined — var è hoisted E inizializzata a undefined
var x = 5;
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 5;
var viene hoisted e inizializzata immediatamente a undefined. let e const vengono hoisted ma restano non inizializzate finché l’esecuzione non raggiunge davvero la loro dichiarazione. Il tratto di codice tra l’inizio dello scope e quella dichiarazione — dove la variabile esiste ma toccarla lancia un errore — è la Temporal Dead Zone (TDZ).
Perché esiste la TDZ
Non è una restrizione arbitraria — intercetta una vera classe di bug. Con var, leggere una variabile prima della sua dichiarazione dà silenziosamente undefined, il che spesso maschera un errore logico (volevi usarla dopo che era stata impostata, ma un refuso o un refactoring ha spostato la lettura prima). La TDZ trasforma quell’undefined silenzioso in un errore rumoroso e immediato esattamente nel punto in cui hai provato a usare il valore non ancora inizializzato.
Dove compare nel codice reale
Oscurare accidentalmente una variabile esterna
let value = "esterno";
function example() {
console.log(value); // lancia — non "esterno"!
let value = "interno";
}
example();
È facile presumere che console.log(value) legga il value esterno, dato che è ciò che succederebbe con var. Non è così — let value dentro example viene hoisted in cima alla funzione, oscurando la variabile esterna per tutto il corpo della funzione, inclusa la riga prima della propria dichiarazione. Quella riga è nella TDZ per il value interno, quindi lancia un errore invece di leggere silenziosamente quello esterno.
Un parametro di default che fa riferimento a se stesso
function greet(name = greeting, greeting = "Ciao") {
return `${greeting}, ${name}`;
}
greet(); // ReferenceError: Cannot access 'greeting' before initialization
I parametri di default vengono valutati da sinistra a destra, e ognuno è nella TDZ finché il proprio default non è stato assegnato — il default di name prova a leggere greeting prima che greeting sia stato inizializzato. Riordinare i parametri risolve il problema.
Anche le dichiarazioni class sono nella TDZ
new Vehicle(); // ReferenceError — le classi sono hoisted ma restano nella TDZ
class Vehicle {}
Questo sorprende chi viene da altri linguaggi orientati agli oggetti dove le dichiarazioni di classe sono tipicamente disponibili in tutto il file. In JavaScript, class si comporta come let/const per questo scopo, non come una dichiarazione di funzione (che è completamente hoisted e chiamabile prima della sua posizione testuale).
var vs let/const: confronto diretto
var |
let / const |
|
|---|---|---|
| Hoisted | Sì | Sì |
| Inizializzata al momento dell’hoist | Sì, a undefined |
No — resta nella TDZ |
| Accesso prima della dichiarazione | Restituisce undefined |
Lancia ReferenceError |
| Scope | Di funzione | Di blocco |
FAQ
❓ La TDZ è un bug di JavaScript o un bug del mio codice?
Né l’uno né l’altro, esattamente — è il linguaggio che fallisce deliberatamente in modo rumoroso
invece di restituire silenziosamente undefined. Se ci incappi, quasi sempre c’è un vero problema
di ordinamento nel tuo codice che vale la pena correggere (una variabile oscurata, un parametro di
default nell’ordine sbagliato), non qualcosa da aggirare.
❓ La TDZ vale anche per i parametri di funzione?
Sì — i parametri sono essenzialmente scope-let all’interno della funzione, il che è esattamente
il motivo per cui l’esempio del parametro di default auto-referenziante sopra lancia un errore.
❓ typeof evita di scatenare la TDZ, come fa per le variabili non dichiarate?
No, ed è un’eccezione sottile ma importante: typeof unaVariabileNonDichiarata normalmente
restituisce in sicurezza "undefined" per una variabile mai dichiarata affatto, ma typeof su
una variabile let/const ancora nella sua TDZ lancia lo stesso ReferenceError — la TDZ rende
insicuro anche typeof per quella specifica variabile.
Conclusione
La Temporal Dead Zone è JavaScript che converte una categoria di bug silenzioso — leggere una var prima della sua assegnazione prevista e ottenere undefined senza nessun avviso — in un ReferenceError rumoroso e immediato per let, const e class. I tre pattern sopra (oscuramento accidentale, parametro di default auto-referenziante, e riferimento anticipato a una classe) coprono quasi ogni caso reale; una volta riconosciuta la forma, la correzione è di solito solo riordinare il codice.
Riferimenti

Co-Fondatore & CTO presso PAPION. Senior full-stack engineer specializzato in React, TypeScript, Node.js e sicurezza applicativa.