Torna al blog

Nullish Coalescing vs Logical OR in JavaScript

Un'analisi approfondita delle differenze tra gli operatori ?? (nullish coalescing) e || (logical OR) in JavaScript, con esempi pratici e casi d'uso per scegliere quello giusto nei tuoi progetti.

Edoardo Midali

Edoardo Midali

Developer · Content Creator

8 min di lettura
Nullish Coalescing vs Logical OR in JavaScript

Nullish Coalescing e Logical OR: Due Operatori Apparentemente Simili

Gli sviluppatori JavaScript conoscono da tempo l'operatore logical OR (||), utilizzato comunemente per fornire valori di fallback quando una variabile potrebbe essere falsy. Tuttavia, con l'introduzione dell'operatore nullish coalescing (??) in ES2020, molti sviluppatori si trovano a chiedersi quale sia l'operatore più appropriato da utilizzare in diverse situazioni.

Sebbene a prima vista possano sembrare intercambiabili, questi due operatori presentano differenze sostanziali nel loro comportamento, e scegliere quello sbagliato può portare a bug sottili ma significativi nelle applicazioni JavaScript.

Logical OR (||): Il Veterano con i suoi Limiti

L'operatore logical OR (||) è stato a lungo utilizzato in JavaScript come meccanismo per fornire valori predefiniti:

const username = inputValue || "Utente Anonimo";

Questo pattern è comune e funziona in molti casi, ma presenta un problema fondamentale: || considera falsy tutti i seguenti valori:

  • false
  • 0 (zero numerico)
  • '' (stringa vuota)
  • null
  • undefined
  • NaN

Questo significa che se inputValue è uno dei valori sopra elencati, l'operatore || lo sostituirà con "Utente Anonimo", anche quando il valore potrebbe essere intenzionalmente 0 o una stringa vuota.

Nullish Coalescing (??): La Soluzione Moderna

Per risolvere questo problema, ES2020 ha introdotto l'operatore nullish coalescing (??), che funziona in modo simile a || ma con una differenza cruciale: considera "nullish" solo null e undefined.

const contatore = inputValue ?? 0;

In questo esempio, se inputValue è undefined o null, il contatore sarà impostato a 0. Tuttavia, se inputValue è 0, una stringa vuota, false o NaN, quel valore verrà mantenuto, rispettando così l'intenzione di assegnare esplicitamente valori che in altri contesti sarebbero considerati falsy.

Confronto Diretto: Esempi Pratici

Per comprendere meglio le differenze, vediamo alcuni esempi concreti:

Esempio 1: Gestione di Input Numerici

function calcolaPrezzo(prezzo, sconto) {
  // Con OR (||)
  const scontoOR = sconto || 0;

  // Con Nullish Coalescing (??)
  const scontoNC = sconto ?? 0;

  return prezzo - prezzo * (sconto / 100);
}

// Cosa succede se lo sconto è 0?
calcolaPrezzo(100, 0);

Con l'operatore ||, uno sconto di 0 verrebbe trattato come falsy e sostituito con il valore predefinito 0 (risultando comunque in 0). Con l'operatore ??, lo sconto di 0 verrebbe mantenuto come 0. In questo caso specifico, il risultato finale è lo stesso, ma il comportamento è concettualmente diverso.

Esempio 2: Configurazione con Valori Booleani

function inizializzaApp(config) {
  const mostraAnimazioni = config.mostraAnimazioni || true; // Problematico
  const usaModalità = config.usaModalità ?? "chiaro";

  // ...
}

// Se config.mostraAnimazioni è impostato a false
inizializzaApp({ mostraAnimazioni: false, usaModalità: undefined });

In questo esempio, c'è un problema critico: anche se l'utente ha esplicitamente impostato mostraAnimazioni a false, l'operatore || lo tratterà come falsy e lo sostituirà con true, ignorando completamente la preferenza dell'utente.

Al contrario, ?? userebbe "chiaro" solo se usaModalità fosse null o undefined.

Esempio 3: Gestione delle Stringhe

function salutaUtente(nome) {
  // Con OR (||)
  const messaggioOR = `Ciao, ${nome || "Ospite"}!`;

  // Con Nullish Coalescing (??)
  const messaggioNC = `Ciao, ${nome ?? "Ospite"}!`;

  return messaggio;
}

// Se l'utente inserisce una stringa vuota
salutaUtente("");

Con ||, una stringa vuota verrebbe sostituita con "Ospite". Con ??, la stringa vuota verrebbe mantenuta, risultando in "Ciao, !". A seconda del contesto applicativo, uno di questi comportamenti potrebbe essere più desiderabile dell'altro.

Quando Usare Nullish Coalescing (??)

L'operatore nullish coalescing è generalmente più appropriato quando:

  1. I valori come 0, stringa vuota, o false sono valori validi e intenzionali che desideri preservare.

  2. Stai lavorando con API o input di form dove un utente potrebbe intenzionalmente inserire 0 o una stringa vuota.

  3. Hai bisogno di distinguere tra un valore non fornito (undefined) o esplicitamente nullo (null) e un valore fornito che è falsy.

// Uso corretto di ??
const volume = configurazioneAudio.volume ?? 50; // 0 è un volume valido
const messaggio = utente.messaggioPersonalizzato ?? "Messaggio predefinito"; // "" potrebbe essere intenzionale
const showTooltip = opzioni.mostraTooltip ?? true; // false è una scelta valida

Quando Usare Logical OR (||)

L'operatore logical OR resta utile in scenari dove:

  1. Vuoi filtrare effettivamente tutti i valori falsy, non solo null e undefined.

  2. Stai implementando pattern di short-circuit evaluation per eseguire un'operazione solo se una condizione è vera.

  3. Lavori con API legacy che potrebbero utilizzare qualsiasi valore falsy per indicare "nessun valore".

// Uso corretto di ||
const nomeVisualizzato = utente.nome || "Anonimo"; // Preferisci "Anonimo" a una stringa vuota
isValid() || mostraErrore(); // Esegui mostraErrore solo se isValid è falso
const result = cachedValue || calculateExpensiveValue(); // Calcola solo se necessario

Combinare i Due Operatori

In alcuni casi, potrebbe essere necessario utilizzare entrambi gli operatori per ottenere il comportamento desiderato:

// Utilizzo combinato
function processaInput(input) {
  // Prima verifica se l'input è nullish, in tal caso usa un oggetto vuoto
  // Poi controlla se la proprietà value esiste, altrimenti usa "default"
  const valore = (input ?? {}).value || "default";

  return valore;
}

Comportamento con le Catene di Operatori

Entrambi gli operatori possono essere concatenati per gestire casi più complessi:

// Catena di OR
const risultato1 =
  valorePrimario || valoreSecondario || valorePredefinitoFinale;

// Catena di Nullish Coalescing
const risultato2 =
  valorePrimario ?? valoreSecondario ?? valorePredefinitoFinale;

È importante notare che non è possibile mescolare direttamente ?? e || senza parentesi esplicite, per evitare ambiguità:

// Questo genererà un errore di sintassi
const errore = a || b ?? c;

// Questo è corretto
const corretto = (a || b) ?? c;
// oppure
const corretto2 = a || (b ?? c);

Prestazioni e Considerazioni sul Browser

Dal punto di vista delle prestazioni, entrambi gli operatori sono altamente ottimizzati nei moderni motori JavaScript e non presentano differenze significative nell'esecuzione.

Per quanto riguarda il supporto dei browser, l'operatore ?? è relativamente recente (ES2020), quindi potrebbe non essere supportato nei browser più datati. Se la compatibilità è una preoccupazione, considera l'utilizzo di transpiler come Babel per convertire il codice in una versione compatibile.

// Supporto browser per ??
const supporto = navigator.userAgent.includes("Chrome") ?? false;

// Alternativa compatibile pre-ES2020
const supportoCompatibile =
  navigator.userAgent.includes("Chrome") !== null &&
  navigator.userAgent.includes("Chrome") !== undefined
    ? navigator.userAgent.includes("Chrome")
    : false;

Bug Comuni e Come Evitarli

1. Assumere che ?? sia sempre la scelta migliore

Un errore comune è sostituire acriticamente tutti gli usi di || con ??. In realtà, in alcuni casi potresti effettivamente voler filtrare tutti i valori falsy:

// Se vuoi una stringa non vuota, || è corretto
const nomeUtente = input.nome || "Sconosciuto";

// Se 0 è un valore valido ma vuoi escludere stringhe vuote
const lunghezza = input.lunghezza || 10;

2. Dimenticare che ?? accetta solo null e undefined

// Questo potrebbe portare a bug
function processaDati(dati) {
  // Se dati è una stringa vuota o 0, verrà mantenuto, non sostituito
  const valoriDaElaborare = dati ?? valoriPredefiniti;
  // ...
}

3. Confusione con la valutazione a corto circuito

Entrambi gli operatori utilizzano la valutazione a corto circuito, ma su condizioni diverse:

// || esegue seconda() solo se prima() è falsy
prima() || seconda();

// ?? esegue seconda() solo se prima() è null o undefined
prima() ?? seconda();

Casi d'Uso Avanzati e Pattern

Destructuring con Valori Predefiniti

// Con ||
const { nome, età, ruolo = "Utente" } = utente || {};

// Con ??
const { id, email } = datiUtente ?? { id: generaId(), email: "" };

Guards per Navigazione Sicura degli Oggetti

// Navigazione sicura con ??
const città = utente?.indirizzo?.città ?? "Sconosciuta";

Factory Functions con Configurazione Predefinita

function creaWidget(opzioni) {
  return {
    larghezza: opzioni?.larghezza ?? 300,
    altezza: opzioni?.altezza ?? 200,
    colore: opzioni?.colore || "blue", // Intenzionalmente || per escludere stringa vuota
    visibile: opzioni?.visibile ?? true, // ?? per consentire false esplicito
  };
}

Conclusione: Scegliere l'Operatore Giusto per il Contesto Giusto

La scelta tra nullish coalescing (??) e logical OR (||) dipende essenzialmente dal comportamento desiderato per i valori falsy che non sono null o undefined.

Ricapitoliamo:

  • Usa ?? quando: vuoi distinguere specificamente tra "valore non fornito" (null/undefined) e valori falsy intenzionali (0, '', false).

  • Usa || quando: vuoi un comportamento più "permissivo" che consideri qualsiasi valore falsy come meritevole di essere sostituito con un'alternativa.

La comprensione delle sottili differenze tra questi operatori può sembrare un dettaglio minore, ma è proprio questo tipo di consapevolezza che distingue il codice JavaScript robusto e prevedibile dal codice soggetto a bug difficili da individuare.

In un linguaggio dinamico come JavaScript, scegliere gli operatori giusti è una parte fondamentale della scrittura di codice che funziona come previsto in tutti gli scenari possibili.

Risorse Utili