Espressioni Logiche in JavaScript

Concetti Chiave
  • Gli operatori logici in JavaScript sono utilizzati per combinare valori booleani e restituire un risultato booleano.
  • Gli operatori &&, ||, e ! eseguono operazioni di algebra booleana.
  • Gli operatori logici sono spesso utilizzati in combinazione con gli operatori relazionali per creare espressioni più complesse.

Espressioni Logiche

Gli operatori logici &&, ||, e ! eseguono operazioni di algebra booleana e sono spesso utilizzati in combinazione con gli operatori relazionali per combinare due espressioni relazionali in un'unica espressione più complessa. Questi operatori sono descritti nelle sottosezioni che seguono.

AND Logico

L'operatore && può essere compreso a tre livelli diversi. Al livello più semplice, quando utilizzato con operandi booleani, && esegue l'operazione Boolean AND sui due valori: restituisce true se e solo se sia il suo primo operando che il suo secondo operando sono true. Se uno o entrambi questi operandi sono false, restituisce false.

&& è spesso usato come congiunzione per unire due espressioni relazionali:

x === 0 && y === 0   // true se, e solo se, x e y sono entrambi 0

Le espressioni relazionali valutano sempre a true o false, quindi quando usato in questo modo, l'operatore && stesso restituisce true o false. Gli operatori relazionali hanno precedenza più alta di && (e ||), quindi espressioni come queste possono essere scritte in sicurezza senza parentesi.

Ma && non richiede che i suoi operandi siano valori booleani. Ricordiamo che tutti i valori JavaScript sono o "truthy" o "falsy". I valori falsy sono false, null, undefined, 0, -0, NaN, e "". Tutti gli altri valori, inclusi tutti gli oggetti, sono truthy. Il secondo livello al quale && può essere compreso è come operatore Boolean AND per valori truthy e falsy. Se entrambi gli operandi sono truthy, l'operatore restituisce un valore truthy. Altrimenti, uno o entrambi gli operandi devono essere falsy, e l'operatore restituisce un valore falsy. In JavaScript, qualsiasi espressione o statement che si aspetta un valore booleano funzionerà con un valore truthy o falsy, quindi il fatto che && non restituisca sempre true o false non causa problemi pratici.

Notare che questa descrizione dice che l'operatore restituisce "un valore truthy" o "un valore falsy" ma non specifica quale sia quel valore. Per quello, dobbiamo descrivere && al terzo e ultimo livello. Questo operatore inizia valutando il suo primo operando, l'espressione alla sua sinistra. Se il valore a sinistra è falsy, il valore dell'intera espressione deve essere anch'esso falsy, quindi && semplicemente restituisce il valore a sinistra e non valuta nemmeno l'espressione a destra.

D'altra parte, se il valore a sinistra è truthy, allora il valore complessivo dell'espressione dipende dal valore sul lato destro. Se il valore a destra è truthy, allora il valore complessivo deve essere truthy, e se il valore a destra è falsy, allora il valore complessivo deve essere falsy. Quindi quando il valore a sinistra è truthy, l'operatore && valuta e restituisce il valore a destra:

let o = {x: 1};
let p = null;
o && o.x     // => 1: o è truthy, quindi restituisce il valore di o.x
p && p.x     // => null: p è falsy, quindi lo restituisce e non valuta p.x

È importante comprendere che && può o non può valutare il suo operando destro. In questo esempio di codice, la variabile p è impostata a null, e l'espressione p.x causerebbe, se valutata, un TypeError. Ma il codice usa && in modo idiomatico così che p.x viene valutata solo se p è truthy---non null o undefined.

Il comportamento di && è talvolta chiamato cortocircuito, e a volte potresti vedere codice che sfrutta deliberatamente questo comportamento per eseguire codice condizionalmente. Ad esempio, le seguenti due righe di codice JavaScript hanno effetti equivalenti:

if (a === b) stop();   // Invoca stop() solo se a === b
(a === b) && stop();   // Questo fa la stessa cosa

In generale, devi essere attento ogni volta che scrivi un'espressione con effetti collaterali (assegnazioni, incrementi, decrementi, o invocazioni di funzioni) sul lato destro di &&. Se quegli effetti collaterali si verificano dipende dal valore del lato sinistro.

Nonostante il modo piuttosto complesso in cui questo operatore effettivamente funziona, è più comunemente usato come un semplice operatore di algebra booleana che lavora su valori truthy e falsy.

OR Logico

L'operatore || esegue l'operazione OR booleana sui suoi due operandi. Se uno o entrambi gli operandi sono truthy, restituisce un valore truthy. Se entrambi gli operandi sono falsy, restituisce un valore falsy.

Sebbene l'operatore || sia utilizzato più spesso semplicemente come operatore OR booleano, esso, come l'operatore &&, ha un comportamento più complesso. Inizia valutando il suo primo operando, l'espressione alla sua sinistra. Se il valore di questo primo operando è truthy, fa short-circuit e restituisce quel valore truthy senza mai valutare l'espressione a destra. Se, d'altra parte, il valore del primo operando è falsy, allora || valuta il suo secondo operando e restituisce il valore di quell'espressione.

Come con l'operatore &&, dovremmo evitare operandi del lato destro che includano effetti collaterali, a meno che non vogliamo intenzionalmente utilizzare il fatto che l'espressione del lato destro potrebbe non essere valutata.

Un uso idiomatico di questo operatore è selezionare il primo valore truthy in un insieme di alternative

// Se larghezzaMassima è truthy, usa quello. Altrimenti, cerca un valore nell'
// oggetto preferenze. Se quello non è truthy, usa una costante hardcoded.
let massimo = larghezzaMassima || preferenze.larghezzaMassima || 500;

Si noti che se 0 è un valore legale per larghezzaMassima, allora questo codice non funzionerà correttamente, poiché 0 è un valore falsy.

Prima di ES6, questo idioma è spesso utilizzato nelle funzioni per fornire valori predefiniti per i parametri:

// Copia le proprietà di o in p, e restituisce p
function copia(o, p) {
    p = p || {};  // Se nessun oggetto passato per p, usa un oggetto appena creato.
    // il corpo della funzione va qui
}

In ES6 e successivi, tuttavia, questo trucco non è più necessario perché il valore del parametro predefinito potrebbe essere semplicemente scritto nella definizione della funzione stessa: function copia(o, p={}) { ... }.

NOT Logico

L'operatore ! è un operatore unario; viene posizionato prima di un singolo operando. Il suo scopo è invertire il valore booleano del suo operando. Ad esempio, se x è truthy, !x valuta a false. Se x è falsy, allora !x è true.

A differenza degli operatori && e ||, l'operatore ! converte il suo operando in un valore booleano (usando le regole descritte nel Capitolo 3) prima di invertire il valore convertito. Questo significa che ! restituisce sempre true o false e che è possibile convertire qualsiasi valore x nel suo valore booleano equivalente applicando questo operatore due volte: !!x.

Come operatore unario, ! ha alta precedenza e si lega strettamente. Se si vuole invertire il valore di un'espressione come p && q, è necessario usare le parentesi: !(p && q). Vale la pena notare qui due leggi dell'algebra booleana che possiamo esprimere usando la sintassi JavaScript:

// Leggi di DeMorgan
!(p && q) === (!p || !q)  // => true: per tutti i valori di p e q
!(p || q) === (!p && !q)  // => true: per tutti i valori di p e q