Due modi per chiedere "sono uguali?"
JavaScript ti dà due operatori di uguaglianza: === (stretto) e == (non stretto). Sembrano quasi identici. Si comportano in modo molto diverso.
=== controlla se entrambi gli operandi hanno lo stesso tipo e lo stesso valore. == prima converte gli operandi in un tipo comune, poi li confronta. È da questa conversione, la coercizione di tipo, che nasce gran parte della cattiva fama dell'uguaglianza in JavaScript.
La versione breve: usa === come scelta predefinita. La versione lunga vale la pena leggerla una volta, così sai a cosa stai rinunciando.
Uguaglianza stretta: probabilmente quello che vuoi
=== restituisce true solo quando entrambi i lati coincidono per tipo e valore. Nessuna conversione, nessuna sorpresa:
Se i tipi non coincidono, la risposta è subito false. Se coincidono, JavaScript confronta i valori. Per i primitivi è un confronto di valore; per gli oggetti è un confronto di riferimento (ci torniamo tra poco).
!== è l'operatore di disuguaglianza stretta e segue le stesse regole al contrario.
Uguaglianza non stretta: coercizione travestita
== permette che i due lati abbiano tipi diversi. Prima del confronto, esegue una serie di conversioni per cercare di farli coincidere:
Le regole esatte sono nella specifica, e non sono terribili, ma ricordarle nel mezzo di un debug è un'altra storia. Che "0" == false sia true sorprende anche gli sviluppatori esperti. Lo stesso vale per [] == false (anche questo true, perché l'array viene convertito in "", che viene convertito in 0).
Ecco perché la maggior parte delle guide di stile, e la regola eqeqeq di ESLint, ti dicono di usare === come scelta predefinita. Scambi un carattere in più da digitare con regole che riesci davvero a ricordare.
L'unico pattern utile con ==
C'è un solo idioma con l'uguaglianza non stretta che vale la pena conoscere: x == null restituisce true quando x è null oppure undefined, e false per tutto il resto.
L'equivalente stretto è x === null || x === undefined, che funziona ma si legge peggio. Molte codebase ammettono == null come unica eccezione consentita. Scegli una regola e applicala con coerenza.
Gli oggetti si confrontano per riferimento
Per oggetti, array e funzioni, sia === sia == si pongono la stessa domanda: "entrambi i lati puntano allo stesso oggetto in memoria?". Non "hanno lo stesso contenuto?".
Due oggetti letterali con contenuto identico restano due oggetti diversi. Prima o poi ci cascano tutti.
Per un confronto basato sui valori scrivi una tua funzione di supporto, usi una libreria (lodash.isequal) o serializzi con JSON.stringify nel caso di semplici oggetti piatti:
JSON.stringify funziona solo con dati semplici: ignora funzioni, undefined e symbol, e non garantisce l'ordine delle chiavi tra motori diversi per tutte le forme. È un controllo rapido, non una soluzione generale.
NaN non è uguale a niente
NaN ("not a number") è il valore che JavaScript restituisce quando un'operazione numerica non ha una risposta sensata: 0/0, Number("abc"), Math.sqrt(-1). Entrambi gli operatori di uguaglianza restituiscono false quando uno dei due lati è NaN, anche quando lo sono entrambi:
Per rilevare NaN, usa Number.isNaN(value). Non usare la vecchia funzione globale isNaN: converte prima il suo argomento, quindi isNaN("hello") restituisce true, cosa che non è quasi mai quello che vuoi.
Object.is: quasi ===, con due correzioni
Object.is(a, b) si comporta come === tranne che in due casi limite:
Quasi sempre quello che vuoi è ===. Usa Object.is quando ti serve specificamente trattare NaN come uguale a sé stesso, o distinguere +0 da -0: entrambi casi rari, ma a volte importanti nel codice numerico e negli interni dei framework (React usa Object.is per confrontare lo stato).
Gli operatori di disuguaglianza seguono la stessa divisione
!== è stretto, != non è stretto, e vale lo stesso consiglio:
Usa !== come scelta predefinita. Se hai già ammesso == null, puoi ammettere anche il suo opposto != null per il controllo "né null né undefined".
Una checklist per i confronti
Quando devi confrontare due valori, segui questi passaggi:
- Primitivi, con lo stesso tipo atteso? Usa
===. - Controlli null o undefined?
x == nullva bene se la tua guida di stile lo permette; altrimentix === null || x === undefined. - Controlli
NaN?Number.isNaN(x). - Confronti oggetti per identità?
===fa esattamente quello che vuoi. - Confronti oggetti per contenuto? Scrivi una funzione di supporto, usa una libreria o serializza. Gli operatori integrati non possono aiutarti.
Resta su ===, considera == uno strumento speciale per il caso == null, ed eviterai le stranezze dell'uguaglianza che riempiono le FAQ su JavaScript.
Prossimo passo: gli operatori
L'uguaglianza è solo una parte degli operatori di JavaScript. La prossima pagina passa in rassegna tutti gli altri: aritmetici, logici, di assegnazione e gli operatori abbreviati su cui farai affidamento nel codice di tutti i giorni.
Domande frequenti
Che differenza c'è tra == e === in JavaScript?
=== è l'uguaglianza stretta: restituisce true solo se entrambi gli operandi hanno lo stesso tipo e lo stesso valore. == è l'uguaglianza non stretta: converte gli operandi nello stesso tipo prima di confrontarli. 1 === '1' è false, ma 1 == '1' è true perché == converte prima la stringa in numero.
Devo usare sempre === in JavaScript?
Come scelta predefinita, sì. === è prevedibile e le sue regole si ricordano facilmente. L'eccezione più comune è x == null, che comodamente corrisponde sia a null sia a undefined. La maggior parte dei linter (la regola eqeqeq di ESLint) impone === e permette quel pattern come eccezione facoltativa.
Perché NaN === NaN restituisce false?
Lo standard IEEE 754 stabilisce che NaN non è uguale a niente, nemmeno a sé stesso. Per questo ogni operatore di uguaglianza restituisce false quando c'è di mezzo NaN. Usa Number.isNaN(x) per controllare se un valore è NaN, oppure Object.is(NaN, NaN), che restituisce true.
Come confronto due oggetti in JavaScript?
Sia == sia === confrontano gli oggetti per riferimento, non per contenuto. {a: 1} === {a: 1} è false perché sono oggetti diversi. Per confrontarli per valore devi scrivere un controllo tuo, usare una libreria come isEqual di Lodash, oppure serializzarli con JSON.stringify se sono semplici oggetti piatti.