Pillole di Javascript: conversione tra primitivi

Una delle caratteristiche che differenziano i fluidi dai solidi è la loro capacità di adattarsi alla forma del contenitore nel quale si trovano. Potremmo dire che in Javascript i valori delle variabili sono piuttosto fluidi. Foto di Charlotte Astrid | https://flic.kr/p/6ykXRc (CC BY 2.0)

In un post precedente abbiamo visto quali sono i tipi di dato utilizzati in Javascript. Nel valutare un’espressione a volte è necessario convertire i tipi di dato, per renderli compatibili con i vari operatori. Se questo non viene fatto dal programmatore, ci penserà Javascript, in base alle sue regole di conversione. Dato che queste regole a volte sono poco intuitive è bene conoscerle a fondo, per scrivere codice robusto e facile da leggere. Le possibili conversioni tra tipi sono:

  • da primitivo a primitivo
  • da primitivo a oggetto
  • da oggetto a primitivo

Il caso primitivo/oggetto è trattato in questo post. La conversione da oggetto a primitivo sarà argomento di un post futuro. Vediamo invece le varie conversioni da primitivo a primitivo.

La conversione da un primitivo qualsiasi a numerico avviene secondo le seguenti regole:

  • null è convertito al numero 0 (zero)
  • undefined è convertito al numero NaN
  • se una stringa contiene un numero, questo viene ritornato in formato numerico, altrimenti viene convertito in NaN
  • un booleano è convertito in 1 se true, 0 se false

Il fatto che undefined sia convertito a NaN torna utile quando in una espressione aritmetica si referenzia, probabilmente per errore, una variabile non inizializzata oppure un elemento non esistente di un array o di un oggetto. Infatti in tutti questi casi il valore ritornato è undefined, che convertendo a NaN fa si che il risultato di un eventuale calcolo artimetico sia NaN, e non un valore reale che potrebbe essere non corretto.

Per quanto riguarda i booleani il comportamento è intuitivo, mentre la conversione più interessante avviene per le stringhe: se la stringa contiene la rappresentazione di un valore numerico, questo viene estratto e ritornato con il tipo number. Qualsiasi altra stringa, compresa la stringa vuota, è convertita a NaN. Possiamo quindi scrivere espressioni del tipo:

if ("42" == 42) { /* fai qualcosa */ }

La conversione da un primitivo qualsiasi a stringa segue le regole:

  • null è convertito nella stringa “null”
  • undefined è convertito nella stringa “undefined”
  • number è convertito nella sua rappresentazione in stringa (eventualmente può essere “NaN”, “Infinity” o “-Infinity”)
  • un booleano viene convertito alla stringa “true” o “false”

La conversione a stringa è una delle più semplici, perché in tutti i casi viene creata una rappresentazione del valore sotto forma di stringa. Per null e undefined la rappresentazione è il nome stesso del tipo. Per un numero è il suo valore, che eventualmente può anche essere “NaN”, “Infinity” o “-Infinity”. Un booleano viene convertito nella traduzione inglese del suo valore.

Qualsiasi primitivo può essere convertito a booleano. Un valore che convertito ritorna true è anche detto “truthy”, mentre se ritorna false è detto “falsy”:

  • null e undefined sono entrambi falsy
  • un numero è falsy se è 0 o NaN, tutti gli altri valori sono truthy
  • la stringa vuota è falsy, tutte le altre truthy

La conversione da un primitivo a boolean è forse la più utile. Infatti possiamo usare qualsiasi valore come espressione booleana, in un test condizionale o in espressioni con operatori logici. Il fatto che null e undefined siano convertiti a false permette di controllare facilmente il valore di ritorno di una funzione:

var valore = funzione();
if (valore) { /* fai qualcosa con valore */ }

Avremmo anche potuto scrivere:

if (valore !== null && valore !== undefined) { ... }

Tuttavia nella prima espressione anche una stringa vuota, un numero 0 (sia zero positivo che negativo) e NaN avrebbero fatto fallire il test condizionale, mentre la seconda istruzione è più esplicita e facilita quindi la lettura del codice.

Naturalmente, per quanto riguarda null e undefined, non ha alcun senso convertire un primitivo in questi due valori.

Possiamo anche richiedere esplicitamente la conversione di un primitivo invocando i costruttori dei wrapper come semplici funzioni. Alcuni esempi:

var n = Number("42"); // number 42
var s = String(true); // string "true"
var b = Boolean(null) // boolean false

Notare che non è stato chiamato un costruttore con l’operatore new, ma come una semplice funzione.

In alcuni casi potremmo aver bisogno di maggior controllo sulle regole di conversione. Per convertire una stringa in numero si possono usare le funzioni globali parseInt() e parseFloat(), che ignorano eventuali spazi o caratteri non numerici. Per convertire un numero in stringa si possono usare alcune funzioni dell’oggetto Number: toFixed(), toExponential(), toPrecision(). Inoltre è possibile il metodo toString di Number può prendere come parametro la base in cui rappresentare il numero. Alcuni esempi:

var num = parseInt(" 42abc") // ritorna 42 invece di NaN
(42.123).toFixed(4) // usa quattro cifre decimali fisse: "42.1230"
(42000).toExponential() // converte in notazione esponenziale: "4.2e+4"

Una menzione speciale va al metodo toString degli array: un array vuoto viene convertito in stringa vuota, che a sua volta può essere convertito nel numero zero secondo le regole di conversione automatica. Nel caso di un array che contiene un solo elemento, il metodo toString ritorna l’elemento stesso, convertendolo a stringa secondo le regole di conversione automatica.

Precedente Pillole di Javascript: stringhe Successivo Pillole di Javascript: conversione da oggetto a primitivo