Gli operatori sono il modo in cui le espressioni lavorano
Quasi ogni riga di JavaScript che faccia qualcosa di interessante è costruita con gli operatori: simboli che prendono uno o due valori e ne producono uno nuovo. + somma, === confronta, && combina booleani, ? : sceglie tra due valori. La maggior parte ti sarà familiare da altri linguaggi, ma alcuni hanno stranezze tipiche di JavaScript che conviene conoscere da subito.
Li vediamo per categoria, lasciando per ultimi quelli che userai meno ma che contano quando ti servono.
Operatori aritmetici
Quelli di base si comportano come ti aspetti:
Alcune cose da tenere presenti:
/fa sempre una divisione in virgola mobile.7 / 2è3.5, non3. UsaMath.floor(7 / 2)oMath.trunc(7 / 2)se ti serve un intero.%è il resto, non il vero modulo matematico. Mantiene il segno dell'operando a sinistra:-7 % 3è-1, non2.+è sovraccaricato. Se uno dei due lati è una stringa, concatena invece di sommare:"3" + 1è"31". Ci torniamo tra un attimo.
Incremento e decremento
La differenza tra count++ e ++count conta solo quando usi il valore dell'espressione nella stessa riga. Come istruzione a sé stante fanno la stessa cosa. La maggior parte delle guide di stile preferisce count += 1 per chiarezza.
L'operatore + ha due personalità
Questa frega tutti almeno una volta:
Se uno dei due operandi è una stringa, + diventa concatenazione e converte l'altro lato in stringa. Tutti gli altri operatori aritmetici vanno nella direzione opposta: convertono le stringhe in numeri.
La lezione da portare a casa: quando costruisci stringhe, preferisci i template literal (`price: ${5}`). Quando fai calcoli, assicurati che gli input siano davvero numeri: Number(x) o parseInt(x, 10) li convertono in modo esplicito.
Operatori di confronto
I confronti restituiscono booleani. Ne esistono due varianti: stretta e lasca.
=== e !== sono stretti: stesso valore e stesso tipo. == e != convertono i tipi prima di confrontare, e questo porta a sorprese come null == undefined che vale true o [] == false che vale true. Usa === e !== di default. L'unica eccezione comune è x == null, una comoda scorciatoia per chiedere "x è null oppure undefined?"
Gli operatori di ordinamento funzionano come ti aspetti con i numeri, e in ordine alfabetico con le stringhe:
Il confronto tra stringhe si basa sui codici dei caratteri, quindi distingue tra maiuscole e minuscole. Se ti serve un ordinamento più naturale per le persone, usa String.prototype.localeCompare.
Operatori logici
&& (AND), || (OR) e ! (NOT) combinano booleani, ma sono più interessanti di quanto suggerirebbe la pura algebra booleana.
Il colpo di scena: && e || non restituiscono true o false. Restituiscono uno dei loro operandi. && restituisce il primo valore falsy, oppure l'ultimo se sono tutti truthy. || restituisce il primo valore truthy, oppure l'ultimo se sono tutti falsy.
Ecco perché vedrai pattern come const displayName = user.name || "Guest": prendi la prima cosa che non è vuota. È conciso, ma attenzione: per || anche 0, "" e false fanno scattare il valore di riserva. Se per te sono valori legittimi, usa ?? (più sotto).
Entrambi gli operatori fanno anche cortocircuito: se il lato sinistro determina già il risultato, il lato destro non viene mai eseguito.
L'operatore nullish coalescing ??
?? è come ||, ma scatta solo con null o undefined, non con 0, "" o false.
Usa ?? quando un valore legittimo potrebbe essere falsy: contatori, stringhe vuote, un false esplicito. Usa || quando qualsiasi valore falsy deve significare "mancante". Nel codice moderno ?? è la scelta di default più sicura per i valori opzionali con un default non nullo.
Operatori di assegnazione
= assegna. Le forme composte uniscono l'assegnazione a un altro operatore:
Esistono anche gli operatori di assegnazione logica, ||=, &&= e ??=, che assegnano solo se il valore attuale soddisfa una condizione:
Sono comodi per impostare valori di default senza istruzioni if prolisse.
L'operatore ternario
condition ? a : b è la versione in forma di espressione di if/else. Restituisce a quando la condizione è truthy, altrimenti b:
I ternari danno il meglio quando devi scegliere tra valori brevi. Diventano difficili da leggere appena li annidi: se ti ritrovi a scrivere a ? b : c ? d : e, passa a if/else o a un oggetto di lookup.
typeof e instanceof
typeof restituisce una stringa che descrive il tipo del suo operando:
Due tranelli da memorizzare: typeof null è "object" (un bug del 1995 ormai permanente), e anche gli array restituiscono "object". Usa Array.isArray(x) per gli array e x === null per null.
instanceof controlla se un oggetto è stato creato da un certo costruttore:
Spread e rest usano lo stesso ...
Vedrai ... in due contesti. Come spread, espande un iterabile in singoli elementi:
Come rest, raccoglie più valori in un unico array, di solito nei parametri di una funzione o nella destrutturazione:
Stessa sintassi, compiti opposti. Lo spread spacchetta, il rest raccoglie. Quale dei due sia lo decide il contesto: in una chiamata o in un letterale espande, in una lista di parametri o in un pattern di destrutturazione raccoglie.
Precedenza degli operatori (nel dubbio, usa le parentesi)
Gli operatori hanno una precedenza che decide quale viene eseguito per primo quando li mescoli. La moltiplicazione batte l'addizione, il confronto batte gli operatori logici, e così via:
La tabella completa è lunga e nessuno la ricorda tutta. Una semplice abitudine copre il 99% dei casi: quando mescoli operatori e non sei sicuro dell'ordine, aggiungi le parentesi. Il codice si legge meglio e non dipende dalla memoria di chi lo legge.
Operatori bit a bit (raramente quello che ti serve)
Per completezza: &, |, ^, ~, <<, >> e >>> lavorano sulla rappresentazione binaria degli interi. Li vedrai nel codice grafico, nei protocolli a basso livello e in qualche API che usa maschere di bit come flag.
Un trucco diffuso ma discutibile: n | 0 tronca un numero a un intero a 32 bit, e un tempo se ne abusava come Math.trunc più veloce. Non farlo: Math.trunc è più chiaro e funziona anche con numeri fuori dall'intervallo a 32 bit.
Prossimo passo: if/else
Gli operatori producono valori; if/else usa quei valori per scegliere quale ramo di codice eseguire. Gran parte di ciò che farai con gli operatori di confronto e logici visti sopra sarà passare i loro risultati alle condizioni, ed è proprio l'argomento della prossima pagina.
Domande frequenti
Quali sono i principali operatori in JavaScript?
JavaScript ha operatori aritmetici (+, -, *, /, %, **), di confronto (===, !==, <, >), logici (&&, ||, !), di assegnazione (=, +=, -=) e alcuni speciali come il ternario ? :, typeof e il nullish coalescing ??. Sono i mattoni con cui costruisci le espressioni e controlli il flusso del programma.
Che differenza c'è tra == e === in JavaScript?
=== controlla che coincidano sia il valore sia il tipo. == converte i tipi prima di confrontare, quindi 0 == "0" è true ma 0 === "0" è false. Usa === di default: le regole di conversione di == sono abbastanza sottili da causare bug che non noterai nemmeno in code review.
Cosa fa l'operatore ternario in JavaScript?
condizione ? a : b è un if/else su una riga che restituisce un valore. Se condizione è truthy l'espressione vale a, altrimenti b. È utile per espressioni condizionali brevi come const label = count === 1 ? 'item' : 'items', ma i ternari annidati diventano presto difficili da leggere.
Quando conviene usare ?? invece di ||?
|| passa al valore di riserva con qualsiasi valore falsy, compresi 0, "" e false. ?? lo fa solo con null o undefined. Se vuoi che count ?? 10 mantenga uno 0 legittimo, usa ??. Se vuoi davvero che qualsiasi valore falsy attivi il valore di riserva, resta su ||.