I tipi di dato in JavaScript: primitivi vs oggetti
I sette primitivi, gli oggetti, typeof e il famoso «bug» di null
Ogni valore in JavaScript ha un tipo, che dice al motore «che genere di dato è» e cosa ci puoi fare. I tipi si dividono in due grandi famiglie: i primitivi (sette) e gli oggetti. Possiamo fare un parallelo con la chimica: i primitivi sono gli atomi e gli oggetti sono le molecole. Le molecole sono strutture più grandi, costruite mettendo insieme più atomi.
1. I sette primitivi
Un valore primitivo è semplice e immutabile: una volta creato non si può modificare. Eccoli, con un esempio ciascuno:
- string testo: "Ciao"
- number numeri interi e decimali: 42, 3.14
- bigint interi enormi, oltre il limite sicuro di number: 42n
- boolean vero o falso: true / false
- undefined «nessun valore ancora assegnato» (lo mette il motore)
- symbol un valore unico e irripetibile, usato come chiave: Symbol()
- null «vuoto di proposito» (lo metti tu esplicitamente)
Immutabile è come il numero 5. Puoi cambiare la variabile che lo contiene (metterci 6 al posto suo), ma non puoi trasformare il 5 stesso: il 5 resta 5 per sempre. Si sostituisce il valore, non lo puoi modificare.
Fonte: MDN Web Docs: glossario «Primitive» e «JavaScript data types and data structures».
2. Gli oggetti: l'ottava famiglia
L'ottavo tipo è object: un contenitore di coppie chiave-valore. Anche gli array e le funzioni sono, sotto sotto, oggetti. A differenza dei primitivi, gli oggetti sono modificabili (mutabili).
const persona = {
nome: "Ada",
eta: 36
}; // oggetto
const numeri = [1, 2, 3]; // anche un array e' un oggetto
3. Copia vs riferimento: perché la distinzione conta
Non è teoria: cambia il modo in cui i valori si copiano. I primitivi si copiano per valore (nasce una copia indipendente). Gli oggetti si gestiscono per riferimento: la variabile contiene un «indirizzo» che punta all'oggetto, e due variabili possono puntare allo stesso.
let a = 5;
let b = a; // copia indipendente
b = 9;
console.log(a); // 5 (a non cambia)
const o1 = {
v: 1
};
const o2 = o1; // stesso indirizzo, stesso oggetto!
o2.v = 99;
console.log(o1.v); // 99 (cambiato anche o1)
Il tipo primitivo è un numero scritto su un foglietto: se lo ricopi su un secondo foglietto, hai due foglietti indipendenti. L'oggetto è l'indirizzo di una casa: se lo dai a due persone e una ridipinge le pareti, anche l'altra le vedrà ridipinte.
Fonte: MDN Web Docs: «JavaScript data types and data structures» (gli oggetti sono gli unici valori mutabili).
4. typeof: chiedere il tipo di un valore
L'operatore typeof restituisce una stringa che ti dice il tipo di un valore. Comodo per capire con che tipo di dato hai a che fare:
typeof "Ciao" // "string"
typeof 42 // "number"
typeof 42n // "bigint"
typeof true // "boolean"
typeof undefined // "undefined"
typeof Symbol() // "symbol"
typeof {} // "object"
typeof [1, 2, 3] // "object" (anche gli array!)
typeof function(){} // "function"
Attenzione: gli array danno "object", non «array». Per distinguerli si usa Array.isArray().
Fonte: MDN Web Docs: operatore «typeof» (tabella dei valori restituiti).
5. Il quirk di null
Ed ecco la sorpresa più famosa di JavaScript:
typeof null // "object" <- e non "null"!
Perché? È un bug storico, presente fin dalla primissima versione di JavaScript. All'epoca ogni valore era rappresentato da un'etichetta di tipo più il valore: l'etichetta degli oggetti era 0, e null era il puntatore NULL (0x00), che finiva anch'esso con l'etichetta 0 e quindi «object». Una correzione fu proposta, ma respinta per non rompere i siti esistenti: resta così per sempre.
Come si controlla allora se un valore è null? Non con typeof, ma con l'uguaglianza stretta: valore === null.
Bonus: null vs undefined: undefined significa «non ho ancora assegnato niente» (lo mette il motore da solo); null significa «vuoto di proposito» (lo metti tu). Curiosità: null == undefined è true, ma null === undefined è false (sono tipi diversi).
In sintesi
| Tipo | Esempio | typeof restituisce |
|---|---|---|
string |
"Ciao" |
"string" |
number |
42, 3.14 |
"number" |
bigint |
42n |
"bigint" |
boolean |
true |
"boolean" |
undefined |
(non assegnato) |
"undefined" |
symbol |
Symbol() |
"symbol" |
null |
null |
"object" <- quirk! |
object |
{ }, [ ] |
"object" |
Per approfondire
- Spread e Rest operator (...): stesso simbolo, lavori opposti: copiare un oggetto con lo spread: perché la copia è superficiale.
- Destructuring: array, oggetti, default, nested, rest: estrarre valori da array e oggetti in una riga.
Commenti (0)
Registrati per commentare, fare domande e proporre l'argomento della prossima pillola (oppure accedi se hai già un account).
Nessun commento. Sii il primo a scrivere qualcosa.