Due tipi di coercizione
JavaScript è permissivo con i tipi. Quando un operatore riceve un valore del tipo "sbagliato", il linguaggio non lancia un errore: lo converte. Questa conversione si chiama coercizione, e ne esistono due varianti:
- Esplicita: la chiedi tu, con
Number("42"),String(99),Boolean(value). - Implicita: la scatena un operatore senza che tu scriva una conversione, come in
"5" - 2,"" == 0,if (value).
Entrambe finiscono per applicare le stesse regole di conversione sottostanti. La differenza è se a decidere di convertire sei stato tu o il linguaggio. Quasi ogni risultato strano di JavaScript ("5" + 1 === "51", [] == false, null == undefined) nasce da una coercizione implicita che ti coglie di sorpresa.
Il modello mentale: se scrivi Number(...) o String(...) sai cosa stai chiedendo. Se non lo fai, ogni operatore ha la sua opinione su cosa fare, ed è proprio lì che si nascondono i bug.
I tre tipi di destinazione
La coercizione converte sempre verso uno di tre tipi primitivi: string, number o boolean. (Ce n'è un quarto, bigint, ma non riceve mai una conversione automatica da altri tipi.) Tutto il resto delle regole deriva da quale destinazione punta un operatore.
La coercizione a stringa è quella più indulgente: ogni valore ha una forma testuale. Nota che gli oggetti diventano il poco utile "[object Object]", ed è per questo che stampare un oggetto concatenato a una stringa ("user: " + user) quasi non mostra mai quello che volevi. Usa invece JSON.stringify o template literal con campi specifici.
Coercizione a number
La coercizione a numero è più rigida. Le stringhe devono sembrare davvero un numero, altrimenti ottieni NaN:
Le sorprese tra parentesi sono quelle da imparare a memoria:
- La stringa vuota e le stringhe fatte solo di spazi diventano
0, nonNaN. nulldiventa0, maundefineddiventaNaN.- Un array vuoto diventa
0; un array con un solo elemento converte quell'elemento; un array con più elementi diventaNaN.
Se vuoi estrarre un numero da una stringa che contiene altri caratteri, usa parseInt o parseFloat invece di Number:
Passa sempre la base (10) a parseInt. Senza, le stringhe che iniziano con "0x" vengono lette come esadecimali, cosa che raramente è quello che intendevi.
Coercizione a boolean
La coercizione a booleano è la più semplice delle tre. Un breve elenco di valori diventa false; tutto il resto è true.
I valori falsy sono:
false0,-0,0n""(stringa vuota)nullundefinedNaN
Tutto qui. Ogni altro valore, compresi "false", "0", [] e {}, è truthy.
I risultati con array e oggetti vuoti fregano chi arriva da Python, dove le collezioni vuote sono falsy. In JavaScript sono truthy: se vuoi sapere "questo array è vuoto?", controlla esplicitamente arr.length === 0.
La coercizione a booleano avviene ogni volta che un valore compare dove ci si aspetta un booleano: if (...), while (...), l'operatore ternario ? : e gli operatori logici &&, ||, !.
L'operatore + è un caso speciale
La maggior parte degli operatori aritmetici converte gli operandi in numeri. + è diverso: se uno qualsiasi dei due lati è una stringa, + concatena le stringhe. Altrimenti fa una somma numerica.
La valutazione procede da sinistra a destra. 1 + 2 + "3" calcola prima 1 + 2 = 3, poi 3 + "3" = "33". Invece "1" + 2 + 3 parte come stringa e resta stringa: "12", poi "123".
Ecco perché costruire stringhe con + è fragile. I template literal non hanno questo problema:
Il template literal valuta count + 1 come espressione numerica a sé, poi inserisce il risultato. Nessuna coercizione a sorpresa.
Preferisci le conversioni esplicite
Quando devi convertire un tipo, dillo chiaramente. Costa qualche carattere in più ed elimina ogni ambiguità per chi leggerà dopo:
Lo stesso vale per i booleani. !!value funziona ed è diffuso, ma Boolean(value) dice esattamente cosa fa:
Una regola ragionevole: usa le conversioni esplicite nella logica dell'applicazione e riserva le forme brevi (+x, !!x) ai punti in cui la brevità conta e l'intenzione è ovvia dal contesto.
L'operatore == si appoggia molto sulla coercizione
Gli operatori di uguaglianza sono la fonte principale di sorprese legate alla coercizione. == converte prima di confrontare; === no.
Ogni true qui sopra è il risultato di una catena di coercizioni in più passaggi che la maggior parte degli sviluppatori non saprebbe recitare a memoria. È questo il problema: il codice che funziona per caso è codice che prima o poi si rompe. Vedremo le regole complete dell'uguaglianza nella prossima pagina; per ora il messaggio è: usa === per impostazione predefinita, e ricorri a == solo per l'idioma specifico x == null (che cattura sia null sia undefined).
Mettere insieme i pezzi
Un esempio completo che mostra dove la coercizione aiuta e dove fa danni:
Guarda la seconda chiamata. Number("") restituisce 0, non NaN, quindi parsePrice("") restituisce 0, che probabilmente non è ciò che vorrebbe chi usa questa funzione. Se l'input vuoto va rifiutato, aggiungi un controllo esplicito:
Sapere quali valori diventano 0 e quali NaN è proprio il tipo di dettaglio che ti salva da un bug subdolo più avanti.
Cosa portarsi a casa
- La coercizione converte in string, number o boolean a seconda dell'operatore.
+con una qualsiasi stringa diventa concatenazione; ogni altro operatore aritmetico converte in numero.- I valori falsy sono un breve elenco fisso: imparalo a memoria. Tutto il resto è truthy, compresi
[]e{}. Number("")è0,Number([])è0,Number(null)è0, maNumber(undefined)èNaN. Questi casi compaiono in bug reali.- Preferisci
Number(x),String(x),Boolean(x)ai trucchetti impliciti. Il te del futuro ti ringrazierà.
Prossimo passo: gli operatori di uguaglianza
Tutto ciò che la coercizione fa nei confronti vive dentro ==. La prossima pagina mette a confronto ==, === e Object.is, mostra l'unico idioma in cui == è ancora utile e spiega perché i linter segnalano per impostazione predefinita la forma non rigorosa.
Domande frequenti
Cos'è la coercizione dei tipi in JavaScript?
La coercizione dei tipi è la conversione automatica che JavaScript fa di un valore da un tipo all'altro: un numero che diventa stringa, una stringa che diventa numero, o qualsiasi valore che diventa booleano. Avviene in modo implicito quando operatori come +, == o if ricevono un valore del tipo che non si aspettano, e in modo esplicito quando sei tu a chiamare Number(x), String(x) o Boolean(x).
Qual è la differenza tra coercizione implicita ed esplicita?
La coercizione esplicita è quando chiami di proposito una funzione di conversione: Number("42"), String(99), Boolean(value). Quella implicita è quando un operatore scatena la conversione alle tue spalle: "5" - 2 dà 3, "5" + 2 dà "52". L'esplicita è leggibile e prevedibile; l'implicita è la causa della maggior parte dei bug del tipo "perché esce NaN?".
Come converto una stringa in numero in JavaScript?
Usa Number("42") per una conversione rigorosa (restituisce NaN se la stringa non è un numero valido), oppure parseInt("42px", 10) / parseFloat("3.14em") se vuoi leggere un numero dall'inizio di una stringa più disordinata. L'operatore unario + (+"42") fa lo stesso di Number(), ma è più facile non notarlo quando scorri il codice.
Perché [] + [] restituisce una stringa vuota?
[] + [] restituisce una stringa vuota?L'operatore + non ha un'operazione numerica sensata tra due array, quindi JavaScript converte entrambi in stringhe. Gli array diventano stringhe unendo i loro elementi con le virgole, e un array vuoto diventa "". Così [] + [] diventa "" + "", cioè "". È un simpatico trucchetto da festa e un ottimo motivo per non affidarti mai a + con valori non primitivi.