Istruzioni Condizionali in Javascript

Concetti Chiave
  • Le istruzioni condizionali in JavaScript permettono di eseguire blocchi di codice in base al valore di un'espressione.
  • L'istruzione if esegue un blocco di codice se l'espressione è vera.
  • L'istruzione else esegue un blocco di codice se l'espressione è falsa.
  • L'istruzione else if permette di gestire più condizioni in modo sequenziale.
  • L'istruzione switch consente di eseguire blocchi di codice in base al valore di un'espressione, con la possibilità di gestire più casi.
  • Le istruzioni condizionali sono fondamentali per il controllo del flusso del programma e per prendere decisioni basate su condizioni specifiche.

Istruzioni Condizionali

Le istruzioni condizionali eseguono o saltano altre istruzioni a seconda del valore di un'espressione specificata. Queste istruzioni sono i punti decisionali del codice, e sono anche a volte conosciute come "rami" (branch in inglese). Se immaginiamo un interprete JavaScript che segue un percorso attraverso il codice, le istruzioni condizionali sono i luoghi dove il codice si dirama in due o più percorsi e l'interprete deve scegliere quale percorso seguire.

Le seguenti sottosezioni spiegano le istruzioni condizionali di base di JavaScript, l'istruzione if/else, e coprono anche switch, un'istruzione di diramazione multipla più complicata.

Istruzione if

L'istruzione if è l'istruzione di controllo fondamentale che consente a JavaScript di prendere decisioni, o, più precisamente, di eseguire istruzioni in modo condizionato. Questa istruzione ha due forme. La prima è:

if (espressione)
    istruzione

In questa forma, espressione viene valutata. Se il valore risultante è vero, istruzione viene eseguita. Se espressione è falsa, istruzione non viene eseguita. Per esempio:

// Se nomeUtente è null o undefined
if (nomeUtente == null)
    // Imposta nomeUtente a un valore predefinito
    nomeUtente = "John Doe";

O similmente:

// Se nomeUtente è null, undefined, false, 0, "", o NaN, dagli un nuovo valore
if (!nomeUtente) nomeUtente = "John Doe";

Notare che le parentesi attorno all'espressione sono una parte richiesta della sintassi per l'istruzione if.

La sintassi JavaScript richiede una singola istruzione dopo la parola chiave if e l'espressione tra parentesi, ma è possibile usare un blocco di istruzioni per combinare più istruzioni in una. Quindi l'istruzione if potrebbe anche apparire così:

if (!indirizzo) {
    indirizzo = "";
    messaggio = "Si prega di specificare un indirizzo.";
}

La seconda forma dell'istruzione if introduce una clausola else che viene eseguita quando espressione è false. La sua sintassi è:

if (espressione)
    istruzione1
else
    istruzione2

Questa forma dell'istruzione esegue istruzione1 se espressione è vera ed esegue istruzione2 se espressione è falsa. Per esempio:

if (n === 1)
    console.log("Hai 1 nuovo messaggio.");
else
    console.log(`Hai ${n} nuovi messaggi.`);

Quando si hanno istruzioni if annidate con clausole else, è richiesta qualche cautela per assicurarsi che la clausola else vada con l'istruzione if appropriata. Si considerino le seguenti righe:

i = j = 1;
k = 2;
if (i === j)
    if (j === k)
        console.log("i uguale k");
else
    console.log("i non uguale j");    // SBAGLIATO!!

In questo esempio, l'istruzione if interna forma la singola istruzione consentita dalla sintassi dell'istruzione if esterna. Sfortunatamente, non è chiaro (eccetto dall'indizio dato dall'indentazione) con quale if va l'else. E in questo esempio, l'indentazione è sbagliata, perché un interprete JavaScript interpreta effettivamente l'esempio precedente come:

if (i === j) {
    if (j === k)
        console.log("i uguale k");
    else
        console.log("i non uguale j");    // OOPS!
}

Questo problema prende il nome di Dangling Else (l'else pendente). Il problema è che l'else viene sempre associato all'if più vicino che ne è sprovvisto. Quindi, in questo esempio, l'else è associato all'if interno, non a quello esterno. Questo è un errore comune tra i programmatori JavaScript inesperti, e può essere frustrante da risolvere.

La regola in JavaScript (come nella maggior parte dei linguaggi di programmazione) è che per default una clausola else è parte dell'istruzione if più vicina. Per rendere questo esempio meno ambiguo e più facile da leggere, capire, mantenere, e debuggare, si dovrebbero usare le parentesi graffe:

if (i === j) {
    if (j === k) {
        console.log("i uguale k");
    }
} else {  // Che differenza fa la posizione di una parentesi graffa!
    console.log("i non uguale j");
}

Molti programmatori prendono l'abitudine di racchiudere i corpi delle istruzioni if e else (così come altre istruzioni composte, come i cicli while) dentro parentesi graffe, anche quando il corpo consiste di una sola istruzione. Farlo in modo consistente può prevenire il tipo di problema appena mostrato, e si consiglia di adottare questa pratica.

Istruzione else if

L'istruzione if/else valuta un'espressione ed esegue uno di due blocchi di codice, a seconda del risultato. Ma cosa succede quando abbiamo bisogno di eseguire uno di molti blocchi di codice? Un modo per farlo è con un'istruzione else if. else if non è realmente un'istruzione JavaScript, ma semplicemente un idioma di programmazione usato frequentemente che risulta quando vengono usate istruzioni if/else ripetute:

if (n === 1) {
    // Esegui blocco di codice #1
} else if (n === 2) {
    // Esegui blocco di codice #2
} else if (n === 3) {
    // Esegui blocco di codice #3
} else {
    // Se tutto il resto fallisce, esegui blocco #4
}

Non c'è niente di speciale in questo codice. È solo una serie di istruzioni if, dove ogni if seguente è parte della clausola else dell'istruzione precedente. Usare l'idioma else if è preferibile e più leggibile rispetto a scrivere queste istruzioni nella loro forma sintatticamente equivalente, completamente annidata:

if (n === 1) {
    // Esegui blocco di codice #1
}
else {
    if (n === 2) {
        // Esegui blocco di codice #2
    }
    else {
        if (n === 3) {
            // Esegui blocco di codice #3
        }
        else {
            // Se tutto il resto fallisce, esegui blocco #4
        }
    }
}

Istruzione switch

Un'istruzione if causa una diramazione nel flusso di esecuzione di un programma, e si può usare l'idioma else if per eseguire una diramazione multipla. Questa non è la soluzione migliore, tuttavia, quando tutte le diramazioni dipendono dal valore della stessa espressione. In questo caso, è uno spreco valutare ripetutamente quell'espressione in multiple istruzioni if.

L'istruzione switch gestisce esattamente questa situazione. La parola chiave switch è seguita da un'espressione tra parentesi e un blocco di codice tra parentesi graffe:

switch(espressione) {
    istruzioni
}

Tuttavia, la sintassi completa di un'istruzione switch è più complessa di questa. Varie posizioni nel blocco di codice sono etichettate con la parola chiave case seguita da un'espressione e due punti. Quando uno switch viene eseguito, calcola il valore di espressione e poi cerca un'etichetta case la cui espressione valuti allo stesso valore (dove l'uguaglianza è determinata dall'operatore ===). Se ne trova una, inizia ad eseguire il blocco di codice all'istruzione etichettata dal case. Se non trova un case con un valore corrispondente, cerca un'istruzione etichettata default:. Se non c'è un'etichetta default:, l'istruzione switch salta completamente il blocco di codice.

switch è un'istruzione confusa da spiegare; il suo funzionamento diventa molto più chiaro con un esempio. La seguente istruzione switch è equivalente alle ripetute istruzioni if/else mostrate nella sezione precedente:

switch(n) {
case 1:                        // Inizia qui se n === 1
    // Esegui blocco di codice #1.
    break;                     // Fermati qui
case 2:                        // Inizia qui se n === 2
    // Esegui blocco di codice #2.
    break;                     // Fermati qui
case 3:                        // Inizia qui se n === 3
    // Esegui blocco di codice #3.
    break;                     // Fermati qui
default:                       // Se tutto il resto fallisce...
    // Esegui blocco di codice #4.
    break;                     // Fermati qui
}

Si noti la parola chiave break usata alla fine di ogni case in questo codice. L'istruzione break, descritta più avanti nelle prossime lezioni, fa sì che l'interprete salti alla fine (o "esca da") dell'istruzione switch e continui con l'istruzione che la segue. Le clausole case in un'istruzione switch specificano solo il punto di partenza del codice desiderato; non specificano alcun punto finale. In assenza di istruzioni break, un'istruzione switch inizia ad eseguire il suo blocco di codice all'etichetta case che corrisponde al valore della sua espressione e continua ad eseguire istruzioni fino a raggiungere la fine del blocco. In rare occasioni, è utile scrivere codice come questo che "attraversa" da un'etichetta case alla successiva, ma nel 99% dei casi si dovrebbe fare attenzione a terminare ogni case con un'istruzione break. (Quando si usa switch all'interno di una funzione, tuttavia, è possibile usare un'istruzione return invece di un'istruzione break. Entrambe servono a terminare l'istruzione switch e impedire che l'esecuzione continui al case successivo.)

Ecco un esempio più realistico dell'istruzione switch; converte un valore in una stringa in un modo che dipende dal tipo del valore:

function converti(x) {
    switch(typeof x) {
    case "number":            // Converte il numero in un intero esadecimale
        return x.toString(16);
    case "string":            // Restituisce la stringa racchiusa tra virgolette
        return '"' + x + '"';
    default:                  // Converte qualsiasi altro tipo nel modo usuale
        return String(x);
    }
}

Si noti che nei due esempi precedenti, le parole chiave case sono seguite rispettivamente da letterali numerici e stringhe. Questo è il modo in cui l'istruzione switch viene più spesso usata in pratica, ma si noti che lo standard ECMAScript consente a ogni case di essere seguito da un'espressione arbitraria.

L'istruzione switch prima valuta l'espressione che segue la parola chiave switch e poi valuta le espressioni case, nell'ordine in cui appaiono, fino a trovare un valore che corrisponda. Il caso corrispondente è determinato usando l'operatore di identità ===, non l'operatore di uguaglianza ==, quindi le espressioni devono corrispondere senza alcuna conversione di tipo.

Poiché non tutte le espressioni case vengono valutate ogni volta che l'istruzione switch viene eseguita, si dovrebbe evitare di usare espressioni case che contengono effetti collaterali come chiamate di funzioni o assegnamenti. Il corso più sicuro è limitare semplicemente le espressioni case a espressioni costanti.

Come spiegato in precedenza, se nessuna delle espressioni case corrisponde all'espressione switch, l'istruzione switch inizia ad eseguire il suo corpo all'istruzione etichettata default:. Se non c'è un'etichetta default:, l'istruzione switch salta completamente il suo corpo. Si noti che negli esempi mostrati, l'etichetta default: appare alla fine del corpo switch, seguendo tutte le etichette case. Questo è un posto logico e comune per essa, ma può effettivamente apparire ovunque all'interno del corpo dell'istruzione.