Jul 1, 2025

Performance in Javascript: Errori Comuni da Evitare

Torna indietro
Performance in Javascript: Errori Comuni da Evitare

La performance di un'applicazione web è un fattore critico sia per l'esperienza utente che per la SEO (Search Engine Optimization). Un sito lento frustra gli utenti e viene penalizzato dai motori di ricerca. JavaScript, essendo il linguaggio che spesso gestisce l'interattività e la logica sul lato client, può essere una fonte significativa di rallentamenti se non utilizzato in modo efficiente.

Comprendere gli errori comuni e come evitarli è fondamentale per scrivere codice JavaScript che sia non solo funzionale, ma anche veloce e reattivo.

Manipolazione Eccessiva e Inefficiente del DOM

Il DOM (Document Object Model) è l'interfaccia di programmazione per i documenti HTML e XML. Ogni volta che modifichi il DOM, il browser deve ricalcolare il layout (reflow) e ridisegnare la pagina (repaint), operazioni che possono essere costose in termini di prestazioni.

Errori Comuni:

  • Accessi Ripetuti al DOM: cercare lo stesso elemento DOM più volte anziché memorizzarlo in una variabile;
  • Modifiche Loop nel DOM: modificare gli elementi del DOM all'interno di un ciclo for o iterazione, causando reflow/repaint multipli;
  • Uso di Funzioni Inefficienti: utilizzare selettori o metodi DOM costosi quando esistono alternative più veloci (es. innerHTML per grandi blocchi di HTML invece di creare elementi uno per uno).

Come Evitare:

  • Cache delle Referenze DOM: memorizza gli elementi DOM in variabili quando li usi ripetutamente.

    // Cattivo
    document.getElementById("myElement").style.color = "red";
    document.getElementById("myElement").textContent = "Nuovo testo";
    
    // Buono
    const myElement = document.getElementById("myElement");
    myElement.style.color = "red";
    myElement.textContent = "Nuovo testo";
    
  • Operazioni Batch sul DOM: costruisci le modifiche al DOM in memoria (ad esempio, usando DocumentFragment o creando stringhe HTML complete) e poi aggiungile al DOM in un'unica operazione.

    // Cattivo
    const list = document.getElementById("myList");
    for (let i = 0; i < 1000; i++) {
    	const item = document.createElement("li");
    	item.textContent = `Elemento ${i}`;
    	list.appendChild(item); // Ogni appendChild causa un reflow/repaint
    }
    
    // Buono
    const list = document.getElementById("myList");
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 1000; i++) {
    	const item = document.createElement("li");
    	item.textContent = `Elemento ${i}`;
    	fragment.appendChild(item);
    }
    list.appendChild(fragment); // Un solo reflow/repaint
    
  • Utilizza classList o dataset: per aggiungere/rimuovere classi o dati, usa element.classList.add()/remove() o element.dataset.myValue anziché manipolare direttamente l'attributo className o altri attributi con setAttribute.

Bloccare il Thread Principale (Operazioni Sincrone Pesanti)

JavaScript è single-threaded. Questo significa che se una funzione impiega troppo tempo per essere eseguita, blocca l'intero thread principale del browser, rendendo l'interfaccia utente non responsiva ("freezing").

Errori Comuni:

  • Cicli Infiniti o Molto Lunghi: esecuzione di calcoli complessi o iterazioni su dataset enormi in modo sincrono;
  • Operazioni I/O Sincrone: sebbene meno comuni nel browser (dove I/O è generalmente asincrono), tentare di fare richieste di rete o di lettura file in modo sincrono bloccherà l'UI.

Come Evitare:

  • Asincronicità: utilizza Promises, Async/Await, e Callbacks per operazioni che richiedono tempo (API calls, timer, animazioni complesse).
  • Web Workers: per calcoli intensivi che non coinvolgono il DOM, utilizza i Web Workers. Permettono di eseguire script in background in un thread separato, liberando il thread principale dell'UI.

Gestione Inefficiente degli Event Listener

L'aggiunta eccessiva di event listener o la loro gestione non ottimizzata può rallentare la pagina e aumentare il consumo di memoria.

Errori Comuni:

  • Troppi Listener Individuali: aggiungere un listener per ogni singolo elemento in una lista lunga (es. 1000 elementi di lista);
  • Listener Non Rimossi: non rimuovere i listener quando gli elementi a cui sono associati vengono rimossi dal DOM, causando perdite di memoria.

Come Evitare:

  • Event Delegation (Delegazione degli Eventi): invece di attaccare un listener a ogni elemento figlio, attacca un singolo listener a un elemento genitore. Quando un evento si propaga dal figlio al genitore, puoi identificare il figlio originale e agire di conseguenza.
    <ul id="myList">
    	<li>Elemento 1</li>
    	<li>Elemento 2</li>
    	<li>Elemento 3</li>
    </ul>
    
    const list = document.getElementById("myList");
    list.addEventListener("click", function (event) {
    	if (event.target.tagName === "LI") {
    		console.log("Cliccato:", event.target.textContent);
    	}
    });
    
  • Rimuovi Listener: Assicurati di rimuovere i listener con removeEventListener() quando gli elementi non sono più necessari, specialmente nelle Single Page Applications (SPA) dove le viste vengono spesso caricate e scaricate.

Perdite di Memoria (Memory Leaks)

Le perdite di memoria si verificano quando il browser non è in grado di recuperare memoria che non è più necessaria. Questo può portare a rallentamenti progressivi e crash del browser nel tempo.

Errori Comuni:

  • Listener Non Rimosi: come menzionato sopra, listener non rimossi sugli elementi che vengono eliminati;
  • Closure Eccessive/Referenze Circolari: closure che mantengono referenze a oggetti che altrimenti sarebbero garbage-collected;
  • Variabili Globali Accidentali: dimenticare let, const o var porta a variabili globali che non vengono mai pulite.

Come Evitare:

  • Fai attenzione alle Closure: sii consapevole di quali variabili una closure sta "catturando";
  • null le Referenze: quando un oggetto non è più necessario, imposta le sue referenze a null per aiutare il garbage collector;
  • Strumenti per il Debug: utilizza gli strumenti per sviluppatori del browser (scheda "Memory" in Chrome DevTools) per profilare l'utilizzo della memoria e individuare le perdite.

Non Ottimizzare Loop e Algoritmi

La complessità del tuo codice, specialmente all'interno dei cicli, ha un impatto diretto sulla performance.

Errori Comuni:

  • Calcoli Ripetuti nel Loop: eseguire calcoli che non cambiano all'interno di un loop invece di calcolarli una volta fuori dal loop;
  • Algoritmi Inefficienti: utilizzare algoritmi con complessità temporale elevata (es. O(n^2) o peggio) per grandi dataset quando esistono alternative più efficienti (es. O(n log n) o O(n));
  • Accessi Inefficienti agli Array: accedere alla proprietà length di un array ad ogni iterazione di un ciclo for (sebbene i motori moderni lo ottimizzino, è una buona pratica pre-caching).

Come Evitare:

  • Pre-calcola: sposta i calcoli costanti fuori dai loop.

    // Cattivo
    for (let i = 0; i < myArray.length; i++) {
    	console.log("Lunghezza:", myArray.length); // Ricalcolato ad ogni iterazione
    }
    
    // Buono
    const len = myArray.length;
    for (let i = 0; i < len; i++) {
    	console.log("Lunghezza:", len);
    }
    
  • Scegli l'Algoritmo Giusto: valuta la complessità del tuo algoritmo (Big O notation) e scegli l'approccio più efficiente per il problema che stai risolvendo e la dimensione dei dati;

  • Evita Loop Nidificati Non Necessari: riconsidera il tuo approccio se ti trovi con troppi loop nidificati che elaborano grandi quantità di dati.

Conclusione

La performance in JavaScript è un obiettivo continuo che richiede attenzione e consapevolezza delle insidie. Evitando questi errori comuni, dalla manipolazione inefficiente del DOM alla gestione sconsiderata degli eventi e alla scelta di algoritmi suboptimali, sarai in grado di scrivere codice più veloce, più efficiente e che offre un'esperienza utente superiore. Strumenti di profilazione del browser sono i tuoi migliori amici in questa battaglia: usali regolarmente per identificare e risolvere i colli di bottiglia delle prestazioni.