Hoisting in JavaScript: Guida Completa per Principianti

Principiante
JavaScript

Scopri cos'è l'hoisting in JavaScript, come funziona per variabili e funzioni e perché è fondamentale per scrivere codice pulito e senza bug.

Pubblicato
Tag
Web Development javascript es6 hoisting coding-basics

Che cos'è l'Hoisting?

Se stai iniziando a programmare in JavaScript, potresti esserti imbattuto in un comportamento molto strano: l'utilizzo di una variabile o l'invocazione di una funzione prima che siano state effettivamente dichiarate nel codice. In molti linguaggi di programmazione, questo causerebbe un errore immediato di tipo "undefined" o "not declared". In JavaScript, invece, accade qualcosa di particolare chiamato Hoisting.

Il termine inglese hoisting significa letteralmente "sollevamento". In termini tecnici, l'hoisting è il comportamento del motore JavaScript che sposta le dichiarazioni di variabili, funzioni e classi in cima al loro ambito (scope) durante la fase di compilazione, prima che il codice venga effettivamente eseguito.

È fondamentale capire che JavaScript non sposta fisicamente le righe di codice nel tuo file .js. Quello che succede è che il motore JS legge l'intero script e alloca memoria per le dichiarazioni prima di iniziare a eseguire le istruzioni riga per riga. Questo processo avviene in una fase chiamata Creation Phase.

Come funziona l'Hoisting delle Variabili

Non tutte le variabili vengono "sollevate" allo stesso modo. Il comportamento cambia drasticamente a seconda che tu utilizzi la parola chiave var, let o const.

L'hoisting con var

Quando dichiari una variabile con var, JavaScript solleva la dichiarazione, ma non l'inizializzazione. Questo significa che il motore sa che la variabile esiste, ma non ne conosce ancora il valore.

Consideriamo questo esempio:

console.log(nome); // Output: undefined
var nome = "Mario";
console.log(nome); // Output: Mario

Perché non abbiamo ricevuto un errore? Perché per il motore JavaScript, il codice sopra viene interpretato come se fosse scritto così:

var nome; // La dichiarazione viene sollevata in cima
console.log(nome); // La variabile esiste, ma è undefined
nome = "Mario"; // L'assegnazione rimane dove era
console.log(nome); // Ora il valore è Mario

L'hoisting con let e const

Con l'introduzione di ES6 (ECMAScript 2015), sono state introdotte let e const per risolvere i problemi di confusione creati da var. Anche queste variabili sono tecnicamente sollevate, ma con una differenza cruciale: non vengono inizializzate.

Se provi a fare lo stesso esempio con let:

console.log(eta); // Errore: Cannot access 'eta' before initialization
let eta = 25;

Questo accade perché let e const creano quella che viene chiamata Temporal Dead Zone (TDZ). La TDZ è l'area di codice tra l'inizio dello scope e il punto in cui la variabile viene dichiarata. Se provi ad accedere alla variabile all'interno della TDZ, JavaScript lancerà un ReferenceError.

Hoisting delle Funzioni

L'hoisting delle funzioni è uno degli aspetti più potenti e, allo stesso tempo, potenzialmente confusionari di JavaScript. Esistono due modi principali per definire una funzione, e ognuno si comporta diversamente.

Function Declarations

Le dichiarazioni di funzione classiche vengono sollevate completamente. Questo significa che sia il nome della funzione che il suo corpo (il codice tra le parentesi grappee) vengono spostati in cima.

saluta(); // Output: Ciao a tutti!

function saluta() {
    console.log("Ciao a tutti!");
}

In questo caso, puoi chiamare la funzione saluta() in qualsiasi punto del tuo script, anche prima della riga dove l'hai definita. Questo è utile per organizzare il codice, mettendo le chiamate principali in alto e i dettagli implementativi (le definizioni delle funzioni) in fondo al file.

Function Expressions

Quando assegni una funzione a una variabile (Function Expression), l'hoisting segue le regole delle variabili, non quelle delle funzioni.

calcolaSomma(5, 10); // Errore: calcolaSomma is not a function (se usi var) o ReferenceError (se usi let/const)

var calcolaSomma = function(a, b) {
    return a + b;
};

Perché succede? Perché calcolaSomma è trattata come una variabile. Se usi var, la variabile viene sollevata ma è undefined. Cercare di eseguire undefined() causa un errore perché undefined non è una funzione. Se usi let o const, l'errore sarà un ReferenceError a causa della Temporal Dead Zone.

Esempi Pratici e Casi d'Uso

Capire l'hoisting non serve solo a superare i colloqui tecnici, ma a scrivere codice più robusto. Vediamo alcuni scenari reali.

Scenario 1: Organizzazione del Codice

Grazie all'hoisting delle function declarations, puoi strutturare il tuo file in modo logico. Ad esempio, puoi definire la funzione init() all'inizio per avviare l'app, e definire tutte le funzioni helper in fondo.

// Punto di ingresso dell'applicazione
initApp();

function initApp() {
    console.log("App avviata");
    setupEventListeners();
}

function setupEventListeners() {
    console.log("Event listener configurati");
}

Scenario 2: Evitare Bug con const e let

Immagina di lavorare in un team. Se un collega usa var per una variabile globale, potresti accidentalmente usarla prima della dichiarazione senza accorgerti dell'errore, ottenendo undefined e causando bug difficili da traccare. Usando const e let, JavaScript ti "costringe" a dichiarare le variabili prima dell'uso, rendendo il flusso di dati esplicito e prevedibile.

Errori Comuni e FAQ

Perché ricevo undefined invece di un errore?

Se ricevi undefined, probabilmente stai usando var. Ricorda che var solleva la dichiarazione ma non il valore. Il motore JS dice: "Sì, so che esiste una variabile chiamata X, ma non so ancora cosa contiene".

Qual è la differenza tra ReferenceError e TypeError nell'hoisting?

  • ReferenceError: Accade quando cerchi di accedere a una variabile che non è ancora stata inizializzata (tipico di let e const nella TDZ).
  • TypeError: Accade quando cerchi di chiamare qualcosa come una funzione, ma quell'oggetto non è una funzione (tipico di var quando solleva una variabile che poi viene assegnata a una funzione).

L'hoisting è una buona pratica?

In generale, fare affidamento sull'hoisting delle variabili (var) è considerata una cattiva pratica. Rende il codice meno leggibile e più propenso a errori. Tuttavia, l'hoisting delle function declarations è ampiamente accettato e usato per migliorare l'organizzazione del codice.

Prossimi passi

Ora che hai compreso come JavaScript gestisce il sollevamento delle dichiarazioni, sei pronto per approfondire concetti più avanzati che sono strettamente legati allo scope e all'esecuzione del codice:

  1. Scope (Ambito): Studia la differenza tra Global Scope, Function Scope e Block Scope.
  2. Closures: Scopri come le funzioni possono "ricordare" l'ambiente in cui sono state create, un concetto che si basa su come JS gestisce le variabili.
  3. Execution Context: Approfondisci come JavaScript crea l'ambiente di esecuzione (Global Execution Context e Function Execution Context) per capire esattamente cosa succede durante la Creation Phase.

Per continuare a imparare, ti consigliiamo di leggere la documentazione ufficiale di MDN (Mozilla Developer Network) sulla sezione JavaScript, che rimane il punto di riferimento più autorevole per ogni sviluppatore web.