Espressioni Relazionali in JavaScript
Questa lezione descrive gli operatori relazionali di JavaScript. Questi operatori testano una relazione (come "uguale," "minore di," o "proprietà di") tra due valori e restituiscono true
o false
a seconda che tale relazione esista.
Le espressioni relazionali restituiscono sempre un valore booleano, e tale valore è spesso utilizzato per controllare il flusso di esecuzione del programma nelle istruzioni if
, while
, e for
. Le sottosezioni che seguono documentano gli operatori di uguaglianza e disuguaglianza, gli operatori di confronto, e gli altri due operatori relazionali di JavaScript, in
e instanceof
.
- Gli operatori relazionali in JavaScript sono utilizzati per confrontare valori e restituire un risultato booleano.
- Gli operatori di uguaglianza e disuguaglianza verificano se due valori sono uguali o diversi.
- Gli operatori di confronto confrontano i valori numerici o stringa.
- Gli operatori
in
einstanceof
testano la presenza di proprietà e la relazione di prototipo. - L'uguaglianza va intesa in due modi: uguaglianza debole (con
==
) e uguaglianza forte (con===
).
Operatori di Uguaglianza e Disuguaglianza
Gli operatori ==
e ===
verificano se due valori sono uguali, utilizzando due diverse definizioni di uguaglianza. Entrambi gli operatori accettano operandi di qualsiasi tipo, ed entrambi restituiscono true
se i loro operandi sono uguali e false
se sono diversi. L'operatore ===
è conosciuto come operatore di uguaglianza rigorosa (o talvolta operatore di identità), e verifica se i suoi due operandi sono "identici" utilizzando una definizione rigorosa di uguaglianza. L'operatore ==
è conosciuto come operatore di uguaglianza; verifica se i suoi due operandi sono "uguali" utilizzando una definizione più rilassata di uguaglianza che consente conversioni di tipo.
Gli operatori !=
e !==
testano l'esatto opposto degli operatori ==
e ===
. L'operatore di disuguaglianza !=
restituisce false
se due valori sono uguali l'uno all'altro secondo ==
e restituisce true
altrimenti. L'operatore !==
restituisce false
se due valori sono rigorosamente uguali l'uno all'altro e restituisce true
altrimenti. Come vedremo nella prossima lezione, l'operatore !
calcola l'operazione NOT booleana. Questo rende facile ricordare che !=
e !==
stanno per "non uguale a" e "non rigorosamente uguale a."
Gli operatori =
, ==
, e ===
JavaScript supporta gli operatori =
, ==
, e ===
. Assicuriamoci di comprendere le differenze tra questi operatori di assegnazione, uguaglianza e uguaglianza stretta, e facciamo attenzione a usare quello corretto quando scriviamo codice! Anche se è tentante leggere tutti e tre gli operatori come "uguale", può aiutare a ridurre la confusione se leggiamo "ottiene" o "viene assegnato" per =
, "è uguale a" per ==
, e "è strettamente uguale a" per ===
.
L'operatore ==
è una caratteristica legacy di JavaScript ed è ampiamente considerato una fonte di bug. Dovremmo quasi sempre usare ===
invece di ==
, e !==
invece di !=
.
Come menzionato nelle lezioni precedenti, gli oggetti JavaScript sono confrontati per riferimento, non per valore. Un oggetto è uguale a se stesso, ma non a nessun altro oggetto. Se due oggetti distinti hanno lo stesso numero di proprietà, con gli stessi nomi e valori, non sono comunque uguali. Allo stesso modo, due array che hanno gli stessi elementi nello stesso ordine non sono uguali tra loro.
Uguaglianza stretta
L'operatore di uguaglianza stretta ===
valuta i suoi operandi, poi confronta
i due valori come segue, non eseguendo alcuna conversione di tipo:
- Se i due valori hanno tipi diversi, non sono uguali.
- Se entrambi i valori sono
null
o entrambi i valori sonoundefined
, sono uguali. - Se entrambi i valori sono il valore booleano
true
o entrambi sono il valore booleanofalse
, sono uguali. - Se uno o entrambi i valori è
NaN
, non sono uguali. (Questo è sorprendente, ma il valoreNaN
non è mai uguale a nessun altro valore, incluso se stesso! Per verificare se un valorex
èNaN
, usarex !== x
, o la funzione globaleisNaN()
). - Se entrambi i valori sono numeri e hanno lo stesso valore, sono uguali. Se un valore è
0
e l'altro è-0
, sono anche uguali. - Se entrambi i valori sono stringhe e contengono esattamente gli stessi valori a 16-bit nelle stesse posizioni, sono uguali. Se le stringhe differiscono in lunghezza o contenuto, non sono uguali. Due stringhe possono avere lo stesso significato e lo stesso aspetto visivo, ma essere ancora codificate usando sequenze diverse di valori a 16-bit. JavaScript non esegue alcuna normalizzazione Unicode, e una coppia di stringhe come questa non è considerata uguale agli operatori
===
o==
. - Se entrambi i valori si riferiscono allo stesso oggetto, array, o funzione, sono uguali. Se si riferiscono a oggetti diversi, non sono uguali, anche se entrambi gli oggetti hanno proprietà identiche.
Uguaglianza con conversione di tipo
L'operatore di uguaglianza ==
è simile all'operatore di uguaglianza rigorosa, ma è meno rigoroso. Se i valori dei due operandi non sono dello stesso tipo, tenta alcune conversioni di tipo e prova nuovamente il confronto:
- Se i due valori hanno lo stesso tipo, li testa per l'uguaglianza rigorosa come descritto in precedenza. Se sono rigorosamente uguali, sono uguali. Se non sono rigorosamente uguali, non sono uguali.
-
Se i due valori non hanno lo stesso tipo, l'operatore
==
può ancora considerarli uguali. Si usano le seguenti regole e conversioni di tipo per verificare l'uguaglianza:- Se un valore è
null
e l'altro èundefined
, sono uguali. - Se un valore è un numero e l'altro è una stringa, si converte la stringa in un numero e si prova nuovamente il confronto, usando il valore convertito.
- Se uno dei valori è
true
, lo si converte in 1 e si prova nuovamente il confronto. Se uno dei valori èfalse
, lo si converte in 0 e si prova nuovamente il confronto. - Se un valore è un oggetto e l'altro è un numero o stringa, si converte l'oggetto in un primitivo usando l'algoritmo descritto nella lezione sulla conversione dei tipi e si prova nuovamente il confronto. Un oggetto è convertito in un valore primitivo tramite il suo metodo
toString()
o il suo metodovalueOf()
. Le classi integrate del JavaScript core tentano la conversionevalueOf()
prima della conversionetoString()
, eccetto per la classeDate
, che esegue la conversionetoString()
. - Qualsiasi altra combinazione di valori non è uguale.
- Se un valore è
Come esempio di test per l'uguaglianza, consideriamo il confronto:
"1" == true // => true
Questa espressione restituisce true
, indicando che questi valori dall'aspetto molto diverso sono in realtà uguali. Il valore booleano true
è prima convertito nel numero 1, e il confronto viene fatto nuovamente. Poi, la stringa "1"
è convertita nel numero 1. Poiché entrambi i valori sono ora uguali, il confronto restituisce true
.
Operatori di Confronto
Gli operatori di confronto testano l'ordine relativo (numerico o alfabetico) dei loro due operandi:
-
Minore di (
<
)L'operatore
<
restituiscetrue
se il suo primo operando è minore del secondo operando; altrimenti, restituiscefalse
. -
Maggiore di (
>
)L'operatore
>
restituiscetrue
se il suo primo operando è maggiore del secondo operando; altrimenti, restituiscefalse
. -
Minore o uguale (
<=
)L'operatore
<=
restituiscetrue
se il suo primo operando è minore o uguale al secondo operando; altrimenti, restituiscefalse
. -
Maggiore o uguale (
>=
)L'operatore
>=
restituiscetrue
se il suo primo operando è maggiore o uguale al secondo operando; altrimenti, restituiscefalse
.
Gli operandi di questi operatori di confronto possono essere di qualsiasi tipo. Il confronto può essere eseguito solo su numeri e stringhe, tuttavia, quindi gli operandi che non sono numeri o stringhe vengono convertiti.
Il confronto e la conversione avvengono come segue:
- Se uno degli operandi si valuta in un oggetto, quell'oggetto viene convertito in un valore primitivo; se il suo metodo
valueOf()
restituisce un valore primitivo, viene usato quel valore. Altrimenti, viene usato il valore di ritorno del suo metodotoString()
. - Se, dopo qualsiasi conversione richiesta da oggetto a primitivo, entrambi gli operandi sono stringhe, le due stringhe vengono confrontate, usando l'ordine alfabetico, dove "ordine alfabetico" è definito dall'ordine numerico dei valori Unicode a 16 bit che compongono le stringhe.
- Se, dopo la conversione da oggetto a primitivo, almeno un operando non è una stringa, entrambi gli operandi vengono convertiti in numeri e confrontati numericamente.
0
e-0
sono considerati uguali.Infinity
è maggiore di qualsiasi numero diverso da se stesso, e-Infinity
è minore di qualsiasi numero diverso da se stesso. Se uno degli operandi è (o si converte in)NaN
, allora l'operatore di confronto restituisce semprefalse
. Anche se gli operatori aritmetici non permettono di mescolare valori BigInt con numeri regolari, gli operatori di confronto permettono confronti tra numeri e BigInt.
Ricordiamo che le stringhe JavaScript sono sequenze di valori interi a 16 bit, e che il confronto di stringhe è solo un confronto numerico dei valori nelle due stringhe. L'ordine di codifica numerico definito da Unicode può non corrispondere all'ordine di collazione tradizionale usato in una particolare lingua o località. Notiamo in particolare che il confronto di stringhe è sensibile al maiuscolo/minuscolo, e tutte le lettere ASCII maiuscole sono "minori di" tutte le lettere ASCII minuscole. Questa regola può causare risultati confusi se non ce la aspettiamo. Per esempio, secondo l'operatore <
, la stringa "Zoo" viene prima della stringa "aardvark".
Per un algoritmo di confronto di stringhe più robusto, proviamo il metodo String.localeCompare()
, che tiene conto anche delle definizioni specifiche della località per l'ordine alfabetico. Per confronti insensibili al maiuscolo/minuscolo, possiamo convertire le stringhe in tutto minuscolo o tutto maiuscolo usando String.toLowerCase()
o String.toUpperCase()
.
Sia l'operatore +
che gli operatori di confronto si comportano diversamente per operandi numerici e di stringa. +
favorisce le stringhe: esegue la concatenazione se uno degli operandi è una stringa. Gli operatori di confronto favoriscono i numeri e eseguono il confronto di stringhe solo se entrambi gli operandi sono stringhe:
1 + 2 // => 3: addizione.
"1" + "2" // => "12": concatenazione.
"1" + 2 // => "12": 2 viene convertito in "2".
11 < 3 // => false: confronto numerico.
"11" < "3" // => true: confronto di stringhe.
"11" < 3 // => false: confronto numerico, "11" convertito in 11.
"one" < 3 // => false: confronto numerico, "one" convertito in NaN.
Infine, notiamo che gli operatori <=
(minore o uguale) e >=
(maggiore o uguale) non si basano sugli operatori di uguaglianza o uguaglianza stretta per determinare se due valori sono "uguali". Invece, l'operatore minore-o-uguale è semplicemente definito come "non maggiore di", e l'operatore maggiore-o-uguale è definito come "non minore di". L'unica eccezione si verifica quando uno degli operandi è (o si converte in) NaN
, in quel caso, tutti e quattro gli operatori di confronto restituiscono false
.
L'operatore in
L'operatore in
si aspetta un operando sul lato sinistro che sia una stringa, un simbolo, o un valore che può essere convertito in una stringa. Si aspetta un operando sul lato destro che sia un oggetto. Restituisce true
se il valore del lato sinistro è il nome di una proprietà dell'oggetto del lato destro. Per esempio:
let punto = {x: 1, y: 1}; // Definisce un oggetto
"x" in punto // => true: l'oggetto ha una proprietà chiamata "x"
"z" in punto // => false: l'oggetto non ha una proprietà "z".
"toString" in punto // => true: l'oggetto eredita il metodo toString
let dati = [7,8,9]; // Un array con elementi (indici) 0, 1, e 2
"0" in dati // => true: l'array ha un elemento "0"
1 in dati // => true: i numeri vengono convertiti in stringhe
3 in dati // => false: nessun elemento 3
L'Operatore instanceof
L'operatore instanceof
richiede un operando sinistro che sia un oggetto e un operando destro che identifichi una classe di oggetti. L'operatore restituisce true
se l'oggetto sinistro è un'istanza della classe destra e restituisce false
altrimenti. Vedremo nelle prossime lezioni che, in JavaScript, le classi di oggetti sono definite dalla funzione costruttore che le inizializza. Pertanto, l'operando destro di instanceof
dovrebbe essere una funzione. Ecco alcuni esempi:
let d = new Date(); // Crea un nuovo oggetto con il costruttore Date()
d instanceof Date // => true: d è stato creato con Date()
d instanceof Object // => true: tutti gli oggetti sono istanze di Object
d instanceof Number // => false: d non è un oggetto Number
let a = [1, 2, 3]; // Crea un array con la sintassi letterale di array
a instanceof Array // => true: a è un array
a instanceof Object // => true: tutti gli array sono oggetti
a instanceof RegExp // => false: gli array non sono espressioni regolari
Si noti che tutti gli oggetti sono istanze di Object
. instanceof
considera le "superclassi" quando decide se un oggetto è un'istanza di una classe. Se l'operando sinistro di instanceof
non è un oggetto, instanceof
restituisce false
. Se il lato destro non è una classe di oggetti, genera un TypeError
.
Per comprendere come funziona l'operatore instanceof
, è necessario comprendere la "catena dei prototipi". Questo è il meccanismo di ereditarietà di JavaScript, e sarà descritto nelle prossime lezioni. Per valutare l'espressione o instanceof f
, JavaScript valuta f.prototype
, e poi cerca quel valore nella catena dei prototipi di o
. Se lo trova, allora o
è un'istanza di f
(o di una sottoclasse di f
) e l'operatore restituisce true
. Se f.prototype
non è uno dei valori nella catena dei prototipi di o
, allora o
non è un'istanza di f
e instanceof
restituisce false
.