Scope e Hoisting in JavaScript: il comportamento delle variabili nei moduli e nelle funzioni

by Anastasia P.
SHARE
Scope e Hoisting in JavaScript: il comportamento delle variabili nei moduli e nelle funzioni
© Guida-HTML5.it

Introduzione

Quando si lavora con JavaScript, capire scope e hoisting non serve solo a evitare errori: è fondamentale per scrivere codice leggibile, prevedibile e facile da mantenere. In questo tutorial ci concentriamo su un sotto-argomento molto pratico: come scope e hoisting influenzano il comportamento delle variabili quando usiamo funzioni, blocchi e moduli.

Molti bug in JavaScript nascono da supposizioni sbagliate come: “questa variabile esiste ovunque”, oppure “questa funzione è disponibile solo dopo la dichiarazione”. In realtà il motore JavaScript applica regole precise: alcune dichiarazioni vengono “sollevate” durante la fase di compilazione, altre no; alcune variabili sono visibili solo nel blocco in cui sono definite, altre no. Conoscere questi dettagli aiuta a evitare conflitti di nomi, accessi prematuri e comportamenti inattesi.

Vedremo un esempio completo che mette insieme:

  • variabili globali e locali
  • funzioni dichiarate e funzioni assegnate a variabili
  • scope di blocco con let e const
  • effetti del hoisting sulle funzioni
  • alcuni casi reali da evitare nel codice quotidiano

Codice completo

// Esempio completo: scope e hoisting in un piccolo modulo applicativo

const appName = "Task Manager";

function showAppInfo() {
  console.log("Applicazione:", appName);
  console.log("Versione:", getVersion());

  if (true) {
    let mode = "debug";
    const maxItems = 10;

    console.log("Modalità:", mode);
    console.log("Elementi massimi:", maxItems);
  }

  // console.log(mode); // Errore: mode non è visibile fuori dal blocco
  // console.log(maxItems); // Errore: maxItems non è visibile fuori dal blocco
}

function getVersion() {
  return "1.0.0";
}

showAppInfo();

console.log(userRole);
// ReferenceError: userRole is not defined

let userRole = "admin";

printWelcomeMessage();
// TypeError: printWelcomeMessage is not a function

var printWelcomeMessage = function () {
  console.log("Benvenuto nell´app!");
};

function demoHoisting() {
  console.log(message); // undefined, non errore
  var message = "Hoisting con var";

  console.log(title); // ReferenceError: Cannot access ´title´ before initialization
  let title = "Hoisting con let";

  console.log(getLabel()); // "Etichetta pronta"

  function getLabel() {
    return "Etichetta pronta";
  }
}

demoHoisting();

function processOrder() {
  if (true) {
    const status = "pagato";
    console.log("Stato ordine:", status);
  }

  // console.log(status); // Errore: status non esiste fuori dal blocco

  for (let i = 0; i <3; i++) {
    console.log("Indice ciclo:", i);
  }

  // console.log(i); // Errore: i non è visibile fuori dal for
}

processOrder();

Spiegazione

Il codice precedente mostra diversi comportamenti importanti. Partiamo dal concetto chiave: scope significa “dove una variabile è accessibile”. In JavaScript, lo scope è in gran parte lessicale, cioè determinato da dove scrivi il codice. Una variabile dichiarata dentro una funzione è visibile solo lì dentro; una dichiarata dentro un blocco { ... } con let o const è visibile solo in quel blocco.

1. Variabili globali e locali

appName è dichiarata fuori da ogni funzione, quindi è accessibile da showAppInfo(). Questo è un esempio di variabile con scope più ampio. Invece mode e maxItems esistono solo dentro il blocco if. Appena il blocco termina, smettono di essere accessibili.

2. Hoisting delle funzioni dichiarate

La funzione getVersion() viene chiamata prima di essere definita nel testo del file, ma funziona lo stesso. Questo accade perché le function declaration vengono hoistate completamente: il motore JavaScript le registra prima dell’esecuzione vera e propria.

In pratica, puoi immaginare che JavaScript “conosca” già la funzione quando inizia a eseguire il codice. Per questo motivo, chiamare una funzione dichiarata prima o dopo la sua definizione non cambia il risultato.

3. Hoisting con var: dichiarata sì, inizializzata no

Il caso di message è molto istruttivo. Dentro demoHoisting(), il comando console.log(message) non genera un errore, ma stampa undefined. Perché? Perché var viene hoistato come dichiarazione, ma non come assegnazione. Quindi JavaScript sa che message esiste, ma il suo valore non è ancora stato assegnato.

Questo comportamento è una delle principali fonti di confusione. Una variabile var può sembrare disponibile “troppo presto”, ma in realtà è solo dichiarata, non inizializzata.

4. Hoisting con let e const: zona temporale morta

Quando proviamo a leggere title prima della sua dichiarazione con let, otteniamo un errore. Questo non succede con var. Le variabili dichiarate con let e const sono comunque note al motore JavaScript durante la fase di compilazione, ma non sono accessibili prima della loro inizializzazione. Questo intervallo è spesso descritto come temporal dead zone.

In altre parole: con let e const non hai un valore “vuoto” come con var; hai un errore se provi ad accedere alla variabile troppo presto. Questo comportamento è molto utile perché rende il codice più sicuro e previene bug silenziosi.

5. Funzioni assegnate a variabili

La riga printWelcomeMessage(); genera un errore perché printWelcomeMessage è stata definita come funzione anonima assegnata a una variabile var. In questo caso, non viene hoistata la funzione completa, ma solo la variabile, che inizialmente vale undefined. Di conseguenza, JavaScript tenta di chiamare undefined come se fosse una funzione, e fallisce con un TypeError.

Questo è un punto cruciale: function declaration e function expression non si comportano allo stesso modo rispetto al hoisting.

6. Scope di blocco nei cicli e nelle condizioni

Nel blocco if e nel ciclo for, le variabili dichiarate con let e const sono limitate al loro contesto. È una caratteristica molto utile quando vuoi evitare collisioni di nomi o accessi accidentali a valori interni.

Per esempio, i esiste solo nel ciclo for. Questo rende il codice più pulito rispetto a var, che storicamente ha uno scope di funzione e può “uscire” dal ciclo in modo inatteso.

Best practice

Per lavorare bene con scope e hoisting in JavaScript, conviene seguire alcune regole semplici ma efficaci:

  • Preferisci let e const rispetto a var. Riduci i bug legati al hoisting e allo scope di funzione.
  • Usa const di default quando il riferimento non deve cambiare. È più chiaro e comunica meglio l’intenzione.
  • Dichiara le variabili vicino al punto di utilizzo. Questo rende il codice più leggibile e limita lo scope.
  • Evita di chiamare funzioni prima della loro definizione se sono function expression o arrow function. Anche se il codice può funzionare in alcuni casi, è più facile da leggere se l’ordine è chiaro.
  • Non affidarti al valore undefined di var per gestire la logica del programma. È un comportamento storico, ma spesso fonte di errori.
  • Usa blocchi espliciti per delimitare la visibilità delle variabili nei rami condizionali e nei cicli.

Una buona abitudine è anche quella di pensare alle variabili in termini di “durata” e “visibilità”: quanto deve vivere questo dato? Chi deve poterlo leggere? Se una variabile serve solo dentro una funzione o un blocco, non spostarla più in alto del necessario.

Riepilogo

Scope e hoisting sono due concetti collegati ma distinti. Lo scope definisce dove una variabile è accessibile. L’hoisting definisce quando una dichiarazione viene resa nota al motore JavaScript durante l’esecuzione.

  • function declaration: completamente hoistata, quindi chiamabile prima della definizione.
  • var: hoistata come dichiarazione, ma inizializzata a undefined.
  • let e const: hoistate ma non accessibili prima dell’inizializzazione.
  • scope di blocco: con let e const, le variabili vivono solo nel blocco in cui sono dichiarate.
  • scope di funzione: con var, la visibilità è legata alla funzione, non al blocco.

Se ricordi una sola cosa, ricorda questa: let e const sono la scelta più sicura e moderna per la maggior parte dei casi. Ti aiutano a scrivere codice più prevedibile e a evitare molti problemi di hoisting.

Approfondisci con risorse ufficiali

  • MDN Web Docs - Scope: documentazione chiara sullo scope in JavaScript.
  • MDN Web Docs - Hoisting: spiegazione dettagliata del comportamento di hoisting.
  • MDN Web Docs - let: riferimento ufficiale per le variabili con scope di blocco.
  • MDN Web Docs - const: guida ufficiale all’uso delle costanti.
  • MDN Web Docs - var: utile per capire i limiti e i casi storici da evitare.

Studiare scope e hoisting con esempi concreti è uno dei modi migliori per migliorare la qualità del proprio JavaScript. Una volta interiorizzati questi concetti, sarà molto più facile leggere codice altrui, prevenire bug e progettare funzioni più pulite e sicure.

SHARE