Guida Javascript per principianti: Parte 3

Pubblicato il

Dove eravamo rimasti

Nella lezione precedente ci siamo salutati con le due nuove keyword per la dichiarazione delle nostre variabili introdotte da ECMA6: let e const.

Oggi proseguiremo il nostro percorso andando a scoprire quali tipi di dato esistono in Javascript, a cosa servono e come usarli; non perdiamo altro tempo, iniziamo subito!

In Javascript tutto è un’oggetto!

Salvo che questo che stai leggendo sia il primo articolo sull’argomento, ci sono buone probabilità che avrai già letto questa frase da qualche altra parte; se così non fosse, stai sicuro che prima o poi la leggerai anche altrove 🤭

Ad ogni modo ora non farci troppo caso; ricordati solo di questa frase, in futuro ne capirai il perché.

Tipologie di dato

In Javascript esistono diversi tipi di dato, che a breve andremo ad analizzare nel dettaglio; tuttavia possiamo raggrupparli, almeno in questa prima fase, in due categorie: tipi di dato primitivi e tipi di dato complessi.

Tipi primitivi

I tipi di dato primitivi sono tipi semplici il cui valore viene direttamente assegnato ad una variabile; in parole povere, una variabile a cui abbiamo assegnato un tipo di dato primitivo contiene effettivamente quel valore.

let person = 'Simone';

In questo caso il valore contenuto all’interno della variabile person è effettivamente il valore che abbiamo stabilito in fase di assegnazione (una stringa in questo caso).

Per avere una conferma di ciò, ci basta creare una nuova variabile partendo da quella che già abbiamo, cambiarne il valore e farci restituire a schermo un log della vecchia variabile ed uno della nuova, come nel seguente esempio:

let person = 'Simone';
let anotherPerson = person;
anotherPerson = 'Andrea';
console.log(person);
console.log(anotherPerson);

Quello che ci verrà restituito a schermo sarà la conferma che ogni variabile di tipo primitivo contiene esattamente il valore che gli assegnamo:

Come possiamo vedere, nonostante in fase di creazione della variabile anotherPerson assegnamo ad essa il valore contenuto all’interno della variabile person, dopo la successiva assegnazione del valore Andrea entrambi le nostre variabili contengono esattamente ciò che ci aspettavamo: Simone la prima, Andrea la seconda.

Lo so, potreste pensare che vi stia dicendo l’ovvio, ma datemi tempo e capire il perché di questo preambolo!

Affacciamoci ora un attimo sull’altra tipologia di dato a cui accennavamo prima: i tipi complessi.

Tipi complessi

Se per i tipi di dato primitivi abbiamo visto che all’interno delle variabili che li usano troviamo ovviamente il valore esatto che gli abbiamo assegnato, per i tipi di dato complessi il discorso cambia.

Andiamo dunque a creare un nuovo snippet sulla falsa riga dell’esempio precedente:

let personOne = {
  name: 'Simone',
  surname: 'Gizzi'
};

let personTwo = personOne;

personTwo.name = 'Mario';
personTwo.surname = 'Rossi';

console.log(personOne);
console.log(personTwo);

Come per l’esempio precedente, anche qui andiamo a fare le seguenti operazioni:

  • creiamo una variabile personOne assegnando ad essa un tipo di dato complesso (chiamato Oggetto, che vedremo tra poco) avente come proprietà name e surname, entrambe di tipo Stringa;
  • creiamo una seconda variabile personTwo assegnando ad essa il valore contenuto all’interno della variabile personOne;
  • modifichiamo la proprietà name contenuta all’interno di personTwo con il valore Mario;
  • modifichiamo la proprietà surname contenuta all’interno di personTwo con il valore Rossi;
  • chiediamo a Javascript di restituirci a schermo i valori di entrambi le variabili.

Tuttavia, nonostante le operazioni siano analoghe al precedente esempio, se eseguirai questo codice il risultato che ti troverai davanti sarà davvero inaspettato:

Ed ecco qui che personOne e personTwo sono la stessa persona, nonostante siano persone distinte 🤣

Ora che mi sono divertito a disorientarti abbastanza, posso spiegarti il perché di questo strano comportamento 😇

Ricordi quando ti ho detto che “una variabile a cui abbiamo assegnato un tipo di dato primitivo contiene effettivamente quel valore“? Bene, è arrivato il momento di spiegarti il perché di questa affermazione.

Se per i tipi di dato primitivo funziona così, per i tipi di dato complessi il comportamento è totalmente differente: l’assegnazione di un tipo di dato complesso ad una variabile non fa in modo che la variabile contenga effettivamente quel dato; in realtà essa contiene un riferimento a quel dato, ovvero essa non è nient’altro che un puntamento a quel dato.

Questo apre degli scenari insoliti se non si ha ben chiaro cosa si sta facendo; nell’esempio che abbiamo visto poco fà infatti, quando andiamo a creare la variabile personOne assegnamo ad essa un tipo di dato complesso (ovvero un Oggetto); quello che succede dietro le quinte è che Javascript riserva nella memoria uno spazio in cui andrà a “salvare” il nostro oggetto complesso, dopodiché creerà un puntamento dalla nostra variabile verso quel dato.

Quello che segue è abbastanza “naturale” (per quanto insolito possa sembrare): andando a creare la variabile personTwo ed assegnando ad essa il valore di personOne, di fatto stiamo chiedendo a Javascript di creare un puntamento al nostro oggetto complesso anche nella nuova variabile!

Ne consegue che modificando personTwo, andremo in realtà a modificare il dato complesso a cui essa fà riferimento (ovvero il nostro oggetto complesso)!

A questo punto posso già sentire ronzare nella tua testa la seguente domanda:

“Ma Simone…quindi una variabile contenente un tipo di dato complesso non può essere modificata in alcun modo?”

Certo che si può, ma prima di spiegarti come farlo voglio prendermi del tempo per introdurti, seppur brevemente in questa prima fase, un concetto molto importante che in realtà viene spesso affrontato a livelli piuttosto avanzati: l’immutabilità.

E tu dirai: “Se è un concetto avanzato, perché diavolo me lo fai affrontare ora che sono agli inizi?”

Semplice: ci tengo a trasmetterti quanto prima le best practices sulla scrittura di un buon codice e, soprattutto, quello che secondo me è “il modo giusto” di scrivere il codice. Senza contare che questo concetto avanzato, se correttamente applicato, ti risparmierà non pochi grattacapi nei progetti a cui lavorerai!

Quindi permettimi una breve virata su questo argomento, che approfondiremo nel dettaglio in futuro.

Immutabilità: una finestra sulla programmazione funzionale

Innanzitutto, cos’è la programmazione funzionale?

Se vogliamo dargli una definizione quanto più teorica possibile, dobbiamo attingere dalla matematica che tanto ci piaceva negli anni scolastici, più nello specifico dalla definizione di funzione matematica:

Una funzione matematica è una relazione tra insiemi che associa ad uno o più elementi di un insieme (detto dominio) uno e un solo elemento di un altro insieme (detto codominio).

Ora, volendo semplificare quanto più possibile questo concetto e traslarlo nel pratico, possiamo affermare che la programmazione funzionale è un paradigma attraverso il quale gli sviluppatori scrivono il loro codice in modo tale da non avere effetti collaterali (ho semplificato non poco, ma dovrebbe essere comunque sufficiente a renderti quantomeno l’idea).

“E per Immutabilità invece cosa si intende?”

Beh, la programmazione funzionale si basa sugli oggetti immutabili, ed un oggetto viene definito immutabile quando il suo stato (ergo, il suo valore) non cambia mai dopo la sua creazione. Un oggetto immutabile quindi sarà sempre quello e avrà sempre quel valore, dunque non potrà mai generare un effetto collaterale, proprio perché non cambierà nel tempo!

E così il cerchio si chiude, perché se ben ricordi poco fà abbiamo detto che “la programmazione funzionale è un paradigma attraverso il quale gli sviluppatori scrivono il loro codice in modo tale da non avere effetti collaterali.

A questo punto dovresti cominciare ad aver chiaro il perché io ti stia introducendo alla programmazione funzionale già da ora, ma preferisco comunque sottolineare il punto focale di questa definizione: “…in modo tale da non avere effetti collaterali; già, proprio come l’effetto collaterale che si è verificato quando abbiamo provato a modificare la variabile personTwo!

“Ok, tutto molto bello, ma quindi come modifico ste benedette variabili che vanno per riferimento?”

È arrivato il momento di illuminarti:

let personOne = {
  name: 'Simone',
  surname: 'Gizzi'
};

let personTwo = { ...personOne }; // è qui che accade la magia 😉

personTwo.name = 'Mario';
personTwo.surname = 'Rossi';

console.log(personOne);
console.log(personTwo);

Se eseguiamo ora il nostro codice, questo sarà il nostro output in console:

E grazie ad un pizzico di magia (che in realtà si chiama Spread Operator), finalmente possiamo modificare le variabili contenenti i tipi di dato complessi 🎉

Quello che abbiamo appena fatto è stato utilizzare lo Spread Operator per creare una nuova copia del nostro oggetto complesso ed assegnarla alla variabile personTwo. In questo modo la variabile personTwo non conterrà più il semplice puntamento al nostro oggetto, bensì verrà creata una nuova copia di quell’oggetto e quindi un nuovo puntamento ad esso!

Nota: esistono anche altri metodi per creare copie di oggetti complessi, tra tutti quello più utilizzato prima dell'avvento di ES6 è Object.assign().

Tipi di variabili

Come in ogni altro linguaggio di programmazione, anche in Javascript abbiamo a disposizione diversi tipi di dato; starà a noi capire quando e quali usare in funzione delle esigenze che via via ci si pareranno davanti.

Stringhe

In Javascript, una stringa è una sequenza di caratteri delimitata da singoli apici, doppi apici o backtick.

const a = 'Stringa delimitata da singoli apici';
const b = "Stringa delimitata da doppi apici";
const c = `Stringa delimitata da backtick`;

Requisito fondamentale: ciò che usiamo per “aprire” la stringa dobbiamo usarlo anche per “chiuderla”.

In parole povere, se la stringa è preceduta dai doppi apici, essi devono essere presenti anche al termine di essa. La cosa più semplice da fare quando si lavora con le stringhe è quella di decidere quale delimitatore usare, e scriverlo doppio sin da subito, per poi spostarci nel mezzo ed inserire la nostra stringa.

Nel caso in cui la nostra stringa contenesse però lo stesso carattere usato come delimitatore, abbiamo due strade per ovviare al problema:

Possiamo sfruttare la tecnica di escaping facendo precedere il carattere backslash a quello che ci interessa utilizzare (l’apice singolo nel nostro caso) come in questo caso:

const string = 'Questo CodeMotion è stato l\'evento più bello a cui io abbia mai partecipato';

oppure possiamo semplicemente ripiegare sull’utilizzo di un’altro delimitatore che nella nostra stringa non è presente, come nel seguente caso:

const string = "Questo CodeMotion è stato l'evento più bello a cui io abbia mai partecipato";

Numeri

Javascript non ha una distinzione formale tra numeri interi e numeri decimali; dietro le quinte rappresenta entrambi come numeri a virgola mobile, poi se la parte decimale non è specificata, il numero viene trattato come intero.

Vediamo qualche esempio di numeri:

const zero = 0;
const numeroPositivo = 667;
const numeroNegativo = -73;
const numeroDecimale = 28.08;
const numeroDecimaleNegativo = -22.99;

Rimanendo in tema “numeri”, è opportuno citare un valore numerico speciale piuttosto particolare: NaN, acronimo di Not a Number: esso indica un valore numerico non definito, come nel seguente esempio:

const number = 12 + undefined;

console.log(number);

Se proviamo ad eseguire la seguente istruzione all’interno della console Javascript del browser, questo è ciò che ci si parerà davanti:

Ed eccolo qui: NaN in tutto il suo splendore!

Booleani

Tanto semplice quanto potente, il tipo Booleano può essere visto come un semaforo: se è true il semaforo è verde, mentre se è false allora dobbiamo fermarci, in quanto è rosso.

Spesso i valori booleani vengono utilizzati come flag, ovvero come indicatori utili a capire lo stato in cui si trova un qualcosa; volendo fare un esempio concreto, il seguente pseudo-codice si occuperà di mandare una notifica all’utente qualora quest’ultimo abbia scelto di riceverne:

let notificationEnabled = false;

if (notificationEnabled) {
  sendNotification();
}

Array

Gli array sono un tipo di dato davvero potente: essi ci permettono di creare delle liste di valori ordinate ed indicizzate.

In questa prima fase daremo solo un’occhiata ad alcune delle potenzialità degli array, ma torneremo a presto a parlarne in una lezione dedicata unicamente ad essi.

Facciamo un esempio!

Ipotizziamo di dover salvare nel seguente ordine nome, età, notifiche abilitate, cognome ed email di un utente del nostro sito, potremmo strutturare il nostro array in questo modo:

const values = [ 'Mario', 38, false, 'Rossi', 'mrossi82@gmail.com' ];

Ora, mettiamo il caso che abbiamo necessità di estrapolare il flag di notifiche abilitate per questo specifico utente; sapendo che gli array sono per natura zero-indexed (quindi il primo elemento ha indice 0, il secondo elemento ha indice 1 e così via fino all’ultimo elemento che avrà un indice pari a lunghezza dell'array - 1), ciò che possiamo fare è accedere direttamente al terzo elemento dell’array utilizzando la seguente notazione:

const values = [ 'Mario', 38, false, 'Rossi', 'mrossi82@gmail.com' ];

const notificationEnabled = values[ 2 ]; // ovvero false

if (notificationEnabled) { // in questo caso la notifica non verrà inviata
  sendNotification();
}

Come già accennato poco sopra, ci sarebbe davvero tanto di cui parlare riguardo gli Array, ma per il momento preferisco darti giusto un piccolo assaggio, per poi dedicare un capitolo di questa guida interamente ad essi.

Oggetti

Gli oggetti in Javascript sono una struttura dati complessa ma facilmente consultabile, al pari di un’archivio nella vita reale; facendo un’analogia, pensiamo ad un’archivio di stato, di qualche ente pubblico o delle forze dell’ordine: al loro interno si celano miliardi di dati riguardanti la popolazione di un’intera nazione! Eppure, sapendo anche solo un singolo dato di una persona (ad esempio il codice fiscale, dato abbastanza univoco) si riesce facilmente a trovare il suo fascicolo e quindi a recuperare tutti i dati relativi ad essa.

In Javascript gli oggetti, per grandi linee, funzionano esattamente così: essi hanno una struttura piuttosto semplice, ma offrono potenzialità pressoché infinite.

Questo è un’esempio di un oggetto Javascript piuttosto semplice:

const archive = {
  'mrossi83@gmail.com': {
    name: 'Mario',
    surname: 'Rossi',
    age: 38
  },
  'lucaint@yahoo.com': {
    name: 'Luca',
    surname: 'Interini',
    age: 21
  }
};

Supponendo di avere un’archivio di utenze strutturato in questo modo, ci basterebbe sapere la mail dell’utenza a cui vogliamo risalire per poterci accedere agilmente:

const user = archive[ 'lucaint@yahoo.com' ];

D’altra parte, se invece volessimo estrapolare la lista di email contenute all’interno del nostro archivio, ci basterebbe usare un ciclo per popolare un’array precedentemente creato:

let emailList = [];

for (const email in archive) {
  emailList = [ ...emailList, email ];
}

console.log(emailList);
Nota: Non badate troppo a ciò che ancora non vi è familiare, come in questo caso il costrutto (for..in..); prendete questi esempi come pseudo-codice esemplificativo; torneremo ad affrontare tutte queste cose in maniera più dettagliata nel corso delle lezioni successive.

Null e Undefined

Per quanto possano sembrare simili, questi ultimi due tipi di dato hanno un significato molto diverso, che possiamo racchiudere così:

  • null: la variabile è stata inizializzata da noi con un valore nullo;
  • undefined: la variabile non è stata inizializzata da noi, pertanto Javascript gli assegna valore undefined di default.

Volendo fare un esempio:

let value1 = 10;
let value2 = null;
let value3;

console.log(value1, value2, value3);

Eseguendo questo snippet, l’output che avremo sarà il seguente:

Come possiamo vedere, non avendo impostato alcun valore per la variabile value3, Javascript provvede ad assegnarli come valore undefined

Bene, direi che per oggi è abbastanza; abbiamo visto i tipi di dato primitivi e quelli complessi; abbiamo fatto qualche breve cenno sulla programmazione funzionale e il principio cardine su cui essa si basa, ovvero l’immutabilità; infine, abbiamo analizzato i principali tipi di variabile che è possibile creare in Javascript.

Diversi concetti che seppur possano sembrare banali ai più navigati, non risulteranno altrettanto banali a chi si approccia a questo mondo per la prima volta.

Il mio consiglio quindi è di sperimentare: aprite la console Javascript del browser, o ancora createvi un file html che includa uno script con estensione .js ed iniziate a sperimentare; non importa il come, l’importante è che vi esercitiate, facciate errori ed impariate da essi…solo così farete vostri questi concetti!

Alla prossima 👋