Javascript null vs. undefined

C'è sempre molta confusione attorno alla definizione di `null` e `undefined`.

Rispondendo a una domanda su Stackoverflow ho fatto chiarezza anche per me stesso su queste due "cose".

Prima di tutto occorre specificare che una variabile può essere dichiarata ma non definita, e una volta dichiarata può essere non definita, oppure `null`.

Quando vogliamo sapere se una variabile esiste o meno da qualche parte, il codice in aiuto è il seguente:

if(typeof varName === "undefined") {
	// do something
}

In questo caso se la variabile varName non è stata dichiarata da nessuna parte, il pezzo di codice funziona senza errori. C'è solo un piccolo ma fondamentale problema: la documentazione ufficiale scoraggia questo uso.

Perché?

Il perché è strutturale. Se devi andare a tentoni per sapere se in un punto del tuo codice hai dichiarato o meno una variabile, allora il tuo codice è scritto male e quindi è esposto a bugs. Se sei in un contesto da te creato, come una funzione o un oggetto, saprai bene cosa hai dichiarato e cosa no.

Differente è invece quando cerchi una variabile nello scope globale. Fermo restando che dovresti evitare di smanettare troppo da un contesto verso un altro (per esempio quando modifichi variabili globali dentro una funzione), se proprio hai necessità di sapere se una variabile è presente nel contesto globale puoi usare questo più sicuro metodo:

if( "varName" in window) {
	// do something
}

Fatta questa doverosa premessa, torniamo al significato di `undefined`.

Undefined significa letteralmente `non definita`. Come si fa a dichiarare una variabile non definita? Semplicemente con:

var varName;

In questo caso è dichiarata ma non definita e un

console.log(varName)

ci darà senza errori e senza indugio un bel `undefined`. Per sapere se una variabile, che sappiamo esistere, non è definita è sufficiente:

if(varName === undefined) {
	// do something
}

E' d'obbligo notare la rigorosa uguaglianza (strict equality) onde evitare una pericolosa ugualianza "non rigorosa" ovvero quella tra `null` e `undefined`. Da documentazione, infatti `null` e `undefined` sono in astratto uguali (null == undefined).

Cos'è `null`?

Se noi facciamo `console.log(typeof null)` avremo `object`. Questo sembra essere un errore di progettazione delle prime versioni di JavaScript. In ogni caso avremo `null` quando da una funzione ci aspettiamo un qualcosa di un certo tipo, ma non viene trovata.

Un esempio è quello del metodo `match` di una stringa. Il metodo `match` deve ritornare un array (che è un oggetto) con il risultato della nostra richiesta. Ma se il sistema non trova il risultato richiesto, ritorna un `null`. Un po' come se ci dicesse: "Non ho trovato nulla".

Perché non ritorna un `false` o un array vuoto? Perché proprio `null`?

Non ritorna un `false` per coerenza interna. Io so che quel metodo mi deve ritornare un array, e sinceramente non voglio avere un booleano tra i piedi in un caso più o meno remoto.

Non ritorna un array vuoto anche qui per l'impossibilità ad essere coerenti. In base ai parametri che passiamo al `match` l'array è N-dimensionale. Allora cosa dovrebbe ritornare? Un Array vuoto o un array a N dimensioni contenenti altri array vuoti? No... non buono. E' molto meglio ritornare un `null`, che è come se ci dicesse: "con tutta la buona volontà, io non so che darti! So solo che volevi un oggetto, ma proprio non so come dartelo e come sarebbe stato. Per cui tieni questo nulla!"

Anche qui, semplicemente per sapere se una variabile è `null` basta fare:

if(varName === null) {
	// do Something
}

Infine una piccola nota. In versioni molto vecchie di Javascript, `undefined` non era una parola riservata, ovvero poteva essere sovrascritta da una valore arbitrario. Con un semplice

undefined = true;

potevano crearsi bug nel codice significativi.

Ora quasi non è più un problema e, a meno che non stiamo facendo un gioco non troppo ben protetto lato server, possiamo ignorare questa postilla.

Ma se proprio non possiamo ignorarla, una via efficiente c'è, ed è quella di incapsulare il codice in una funzione che ammette come argomento una variabile che non definiamo, ed utilizzarla per fare i nostri checks. Questa funzione la eseguiamo direttamente utilizzando questa sintassi `(function() {})()` Per esempio:

(function(undefined) {
	var varName;
	if(varName === undefined) {
		// do something
	}
})()