Pillole di Javascript: conversione da oggetto a primitivo

Dopo aver visto le regole di conversione da primitivo a primitivo, vediamo ora l’algoritmo utilizzato da Javascript quando è necessario convertire un oggetto in un valore primitivo.

Su ogni oggetto Javascript possono esserci due metodi utili per la conversione. Il metodo toString(), che ritorna una stringa che rappresenta l’oggetto, e il metodo valueOf(), il quale ritorna un valore numerico che rappresenta l’oggetto. Per entrambi i metodi Javascript offre un comportamento di default. toString() ritorna la stringa “[object Object]”. valueOf() ritorna il riferimento all’oggetto stesso, dato che qualsiasi valore numerico di default non sarebbe significativo.

Il comportamento di default può essere sovrascritto ridefinendo le due funzioni. Non è detto che questo sia sempre possibile, infatti per alcuni oggetti potrebbe non avere senso una conversione a stringa o a numero, oppure potrebbero esserci molte alternative valide. Data la natura debolmente tipizzata del linguaggio, non c’è nessun controllo sull’effettivo tipo di dato ritornato. Quindi è anche possibile anche ridefinire i due metodi facendogli ritornare un oggetto, o un primitivo di tipo diverso da quello atteso.

Un esempio di oggetto che ridefinisce questi due metodi è “Date”, nel quale toString ritorna la rappresentazione in stringa della data contenuta nell’oggetto, mentre valueOf ritorna il timestamp della data (secondi passati dal 01/01/1970). Un esempio pratico della conversione automatica da oggetto a primitivo:

var date = new Date(); // data e ora attuale
var stringa = "Data e ora attuale: " + date; // concatena
var bool = d > getAltraData(); // confronta due timestamp

Nell’esempio viene instanziato un oggetto Date che sarà inizializzato con il timestamp del momento dell’esecuzione. Nella seconda riga l’operatore “+” convertirà Date a stringa, e la concatenerà con la prima stringa. Nella terza riga l’operatore “maggiore di” convertirà Date in numerico, ottenendo il suo timestamp, e la confronterà con un’altra data ottenuta dalla funzione getAltraData().

La conversione automatica in primitivo fatta dagli operatori utilizza i due metodi visti prima. Quando un operatore invoca una conversione, specifica se vuole ottenere un primitivo stringa o numerico. In caso di conversione a stringa, l’algoritmo è il seguente:

Se il tipo richiesto è “string”, invoca il metodo toString().
Se il valore ritornato è un primitivo, convertilo a stringa con le regole di conversione da primitivo a primitivo, e ritorna il valore così ottenuto.
Se invece il metodo non esiste, oppure ritorna un oggetto, ritenta lo stesso algoritmo usando il metodo valueOf().
Se anche questo non esiste oppure ritorna un oggetto allora solleva un TypeError.

In caso di conversione a numerico, l’algoritmo è lo stesso, con la differenza che viene prima provato il metodo valueOf(), se questo fallisce viene provato il metodo toString(). Notare che il comportamento di default di valueOf() è di ritornare l’oggetto stesso, per cui di default la conversione a numerico ritornerà la conversione a numero dell’eventuale primitivo ritornato da toString().

E’ importante ricordare che l’operatore binario “+” impone due eccezioni alla conversione da oggetto a primitivo. Quando l’operatore converte un suo operando oggetto in primitivo, richiede la conversione automatica a numero, ma impone che non venga effettuata la conversione a numero del primitivo ritornato dalla conversione. Se ad esempio il metodo valueOf() ritornasse un booleano, questo sarebbe utilizzato come risultato della conversione, senza convertirlo al tipo numerico. Questa eccezione permette di utilizzare l’operatore per concatenare le rappresentazioni a stringa di due oggetti che non ridefiniscono il metodo valueOf().

La seconda eccezione fatta dall’operatore è specifica per l’oggetto Date. Per questo oggetto viene richiesta la conversione a stringa invece che a numerico. Questo permette di concatenarlo facilmente con un’altra stringa, dove altrimenti sarebbe stata tentata una somma tra il timestamp e l’altra stringa.

Anche l’operatore di confronto non stretto “==”, quando esegue una conversione da oggetto a primitivo, richiede la conversione a numerico, ma non forza il tipo di ritorno.

Precedente Pillole di Javascript: conversione tra primitivi Successivo Pillole di Javascript: operatori di confronto