Guida Javascript per principianti: Parte 2

Pubblicato il

Dove eravamo rimasti

Nella precedente lezione ci siamo salutati con un Hello World stampato a schermo tramite l’utilizzo della funzione alert() di Javascript; oggi proseguiremo il nostro cammino entrando nel vivo del linguaggio, esaminando i tipi di variabili che esso ci mette a disposizione, come compiere le prime operazioni basilari e come scrivere del codice che possa tornarci utile nelle nostre pagine web.

Le variabili

Prima di partire a sperimentare le variabili Javascript, è necessario capire a cosa servono, come funzionano e come possono tornarci utile ai nostri scopi; dunque, iniziamo subito!

Le variabili Javascript (ma questo in realtà vale per tutti i linguaggi di programmazione) sono contenitori di dati situati in una o più porzioni di memoria, e sono appunto destinati a contenere i nostri valori; come facilmente intuibile dal nome, le variabili sono suscettibili a cambiamenti di valore durante l’esecuzione del nostro codice (anche se su questo ci torneremo poi per ulteriori approfondimenti).

Scendendo nel pratico, in Javascript abbiamo a disposizione 3 tipi di variabili, vediamole insieme!

In principio c’era VAR

Fino a qualche tempo fà (precisamente fin quando non è stata introdotta la specifica ECMA6) l’unico modo per definire una variabile in Javascript era tramite l’utilizzo della keyword var; grazie a questa keyword infatti, lo sviluppatore aveva la possibilità di inizializzare una variabile ed assegnarli un valore arbitrario.

Vediamo ora un’esempio pratico:

// utilizzando la keyword VAR, creiamo una variabile che conterrà il risultato di una semplice operazione
var result = 5 + 12;

// ora stampiamo in console il valore contenuto all'interno della variabile
console.log(result);

// output ottenuto
> 17

Come possiamo vedere dal codice di esempio, in questo caso il programmatore ha creato una variabile chiamata result, ed al suo interno ha assegnato come valore il risultato di un’operazione, ovvero 5 + 12; andando a stampare a schermo il valore della variabile infatti, possiamo vedere come essa contenga al suo interno il valore 17, che è appunto il risultato della precedente operazione.

Con l’avvento della specifica ECMA6 però, sono state introdotte altre due keyword adibite alla creazione delle nostre variabili: let e const.

LET e CONST

In questo momento ti starai domandando il perché esistano ad oggi ben 3 modi diversi di dichiarare una variabile… te lo spiegherò tra qualche minuto, prima però vediamo subito un’applicazione pratica utilizzando l’esempio precedente, questa volta utilizzando le keyword let e const:

// utilizzando la keyword LET, creiamo una variabile che conterrà il risultato di una semplice operazione
let result = 5 + 12;

// ora stampiamo in console il valore contenuto all'interno della variabile
console.log(result);

// output ottenuto
> 17
// utilizzando la keyword CONST, creiamo una variabile che conterrà il risultato di una semplice operazione
const result = 5 + 12;

// ora stampiamo in console il valore contenuto all'interno della variabile
console.log(result);

// output ottenuto
> 17

Come noterai, indipendentemente dalla keyword utilizzata, l’output ottenuto dal nostro codice sarà sempre il medesimo!

Ma allora a cosa servono 3 keyword diverse per creare variabili, se poi il risultato è sempre lo stesso? E soprattutto, che differenza c’è tra var, let e const?

Per rispondere a questa domanda è necessario fare la conoscenza di un’altro “attore” principale del linguaggio Javascript: lo scope.

Lo “scope”: questo sconosciuto

Se provi a tradurre la parola “scope” dall’inglese, tra i vari significati proposti sicuramente figurerà la parola “ambito“, che tra tutte è quella che c’azzecca di più per descrivere questo sconosciuto.

In Javascript infatti, quando si parla di scope si intende il contesto di esecuzione all’interno del quale una particolare funzione viene eseguita.

Proprio per questo motivo, Javascript ci mette a disposizione una speciale keyword che può essere usata per referenziare lo scope, e questa keyword è this.

Ricapitolando: qualsiasi funzione in Javascript ha un suo contesto di esecuzione (ovvero ha un suo scope) e sappiamo che per riferirci ad esso possiamo usare la keyword this. Per avere dimostrazione di ciò, possiamo fare subito una prova lanciando il nostro browser, aprendo i Developer Tools e dirigendoci nella sezione “Console”: qui infatti possiamo scrivere del codice Javascript, che verrà subito eseguito dal browser; cliccando dunque all’interno della console e scrivendo la keyword this, una volta premuto INVIO dovremmo trovarci davanti un output simile a questo:

Questo “mostro” è l’oggetto window presente all’interno del browser, e a cui la keyword this in questo caso fà riferimento

Questo “mostro” è l’oggetto window, ovvero l’oggetto a cui la keyword this fà riferimento in questo frangente.

In questo caso stiamo facendo riferimento all’oggetto window perché abbiamo utilizzato la keyword this nello scope più alto che abbiamo a disposizione; proviamo invece ora a fare la stessa prova ma all’interno di una funzione (tranquillo, vedremo le funzioni nel dettaglio nei prossimi articoli):

// dichiaro una funzione che mi permette di creare un nuovo oggetto "Person"
const Person = function(name, surname) {
  this.name = name;
  this.surname = surname;
  this.sayHi = function() {
    alert(`Hi, I'm ${ this.name } ${ this.surname }, nice to meet you!`);
  }
}

// Creo un nuovo oggetto "Person" e passo come argomenti il mio nome e cognome
const me = new Person('Simone', 'Gizzi');

// Invoco il metodo sayHi()
me.sayHi();

Come noterai, in questo caso ho utilizzato la keyword this all’interno della funzione Person, ed in questo caso this non sta più facendo riferimento all’oggetto window, bensì all’oggetto Person; in questa circostanza ho potuto fare in modo di referenziare l’oggetto Person attribuendogli le proprietà name e surname, e successivamente stamparle a schermo tramite la funzione alert().

Sò che è un po’ ostico da digerire, per cui ne approfitto per portarti un’altro esempio pratico.

Crea una nuova pagina HTML, ed al suo interno copia il codice qui sotto:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Scopriamo lo scope!</title>
</head>
<body>
    <h1>Lo scope in azione</h1>

    <button id="my-button">Cliccami :)</button>

    <script type="text/javascript">
     
    const btn = document.querySelector('#my-button');

    btn.addEventListener('click', function(e) {
      console.log(this);
    });

    </script>
</body>
</html>

Copiato il contenuto, salva il file come index.html e trascinalo nel browser per visualizzarlo; dovresti avere davanti un risultato simile a questo:

Questo è ciò che dovresti ottenere visualizzando la pagina index.html appena creata

Ora, aprendo i developer tools (a cui puoi accederci premendo il tasto F12 se sei su Windows o tramite la combinazione dei tasti option + shift + i se sei su Mac) e spostandoti sul tab Console, una volta cliccato sul tasto “Cliccami :)” questo è ciò che vedrai apparire tra i log:

Questo che vedi qui sopra è il risultato dell’istruzione console.log(this); invocata sul click del nostro pulsante

Quello che hai davanti non è nient’altro che l’elemento HTML a cui la keyword this sta facendo riferimento in questa circostanza; se torniamo sul codice incollato poco fà infatti, potrai notare tu stesso che sull’evento di click del pulsante, noi stiamo chiedendo a Javascript di loggare proprio this:

...

const btn = document.querySelector('#my-button');

btn.addEventListener('click', function(e) {
  console.log(this);
});

...

Quando questa porzione di codice verrà eseguita, il comportamento che verrà scaturito sarà dunque il seguente:

  • Verrà inizializzata una variabile di tipo const chiamata btn, al cui interno verrà salvato il riferimento all’elemento HTML avente come id my-button;
  • All’elemento HTML my-button verrà agganciato un EventListener che rimarrà in ascolto dell’evento click;
  • Quando l’evento click si verificherà sull’elemento my-button, verrà invocata la callback definita all’interno dell’EventListener, che nel nostro caso specifico andrà a scrivere una riga di log contenente this nella console Javascript;
  • La console Javascript ci mostrerà a schermo il valore di this.

Come vedi, abbiamo visto 3 scenari differenti (this all’interno della console Javascript, this dentro la definizione di una class, e this associato ad un EventListener agganciato ad un elemento HTML della pagina web); la keyword che abbiamo usato è stata sempre e solo this, ma il significato (o meglio, il contesto di esecuzione) che essa ha assunto è cambiato da situazione a situazione.

Si, lo so, è una cosa contorta e che porta confusione, almeno in questa prima fase; ma non preoccuparti, andando avanti con lo studio, pian piano tutto diverrà più chiaro e familiare.

Bene, ora che abbiamo conosciuto questo strambo tipo che è lo scope, possiamo tornare a ciò che avevamo temporaneamente lasciato in sospeso: LET e CONST.

Le domande con cui ci eravamo lasciati prima erano due:

Ma allora a cosa servono 3 keyword diverse per creare variabili, se poi il risultato è sempre lo stesso?

E soprattutto, che differenza c’è tra var, let e const?

Ora che sappiamo, anche solo per grandi linee, cos’è lo scope e come esso si comporta, possiamo finalmente dare una risposta a questi dubbi finora rimasti appesi.

Ogni sviluppatore Javascript con un po’ di esperienza alle spalle si è ritrovato, lungo la sua carriera, ad avere a che fare con variabili definite con la keyword var; questo perché prima dell’avvento di ECMA6 esso rappresentava l’unico modo che lo sviluppatore aveva a disposizione per creare delle nuove variabili.

Tuttavia questo metodo si è rivelato ben presto di difficile gestione, in quanto le variabili create tramite var hanno un contesto di esecuzione differente in base a come esse vengono dichiarate all’interno del nostro codice; possono essere infatti globally-scoped oppure function-scoped (o locally-scoped), ovvero:

  • globally-scoped quando la variabile viene dichiarata fuori dal corpo di una funzione (ciò significa che qualsiasi variabile dichiarata con var al di fuori di una funzione è disponibile per l’uso globalmente)
  • function-scoped (o locally-scoped) quando la variabile viene dichiarata dentro il corpo di una funzione (ciò significa che la variabile sarà accessibile solo da dentro il corpo della funzione stessa)

Come se questo non bastasse, le variabili create tramite var portano con se un’altro problema: possono essere ri-dichiarate più volte; questo, come potrai immaginare, ha portato tanti sviluppatori a creare non poca confusione nel codice, rendendo quest’ultimo sempre più difficilmente manutenibile.

Vien da se che i mal di testa causati dall’utilizzo di questa keyword non sono stati pochi 🤯

Con l’avvento di ECMA6, le cose sono cambiate notevolmente ed in meglio; sono infatti state introdotte due nuove keyword per la definizione delle variabili: let e const.

Entrambe le keyword hanno il medesimo scopo: creare una nuova variabile; quello che cambia è il modo in cui lo fanno.

Entrambe le keyword creano una variabile di tipo block-scoped, ovvero una variabile che esiste solo all’interno del blocco in cui è stata dichiarata (per intenderci, tutto ciò che c’è tra due parentesi graffe è considerato un blocco); la differenza tra let e const risiede nella possibilità di aggiornare il valore al loro interno; let permette allo sviluppatore di aggiornare il valore al suo interno, mentre const è considerata una costante, ovvero una variabile che conserva al suo interno il valore assegnato ad essa in fase di creazione.

Scendiamo però più nel pratico: se provassimo ad esempio ad eseguire il seguente codice all’interno della console Javascript del browser

function sum (numberOne, numberTwo) {
  let result = numberOne + numberTwo;
}

console.log(result);

otterremmo il seguente errore:

Uncaught ReferenceError: result is not defined

Tuttavia, se cambiassimo leggermente il nostro codice in modo che la dichiarazione della variabile result sia fuori dalla nostra funzione sum (come nel seguente esempio), l’errore sparirebbe:

let result = 0; // qui inizializzo la mia variabile

function sum (numberOne, numberTwo) { // qui dichiaro la mia funzione
  result = numberOne + numberTwo;  // qui aggiorno il valore della mia variabile quando la funzione `sum` viene invocata
}

console.log(result); // qui eseguo un log di `result` ma ancora non ho invocato la mia funzione

sum(2, 3); // qui invoco la mia funzione

console.log(result) // qui eseguo nuovamente un log di `result`, che nel frattempo avrà assunto un'altro valore

Eseguendo ora il codice appena modificato dentro la console Javascript, questo è ciò che vedremo:

Questo è il risultato dell’esecuzione del nostro codice modificato

Come vedi, Javascript ci ha restituito due log distinti:

  • Il primo con valore 0, in quanto proprio 0 è il valore che abbiamo assegnato alla variabile result in fase di dichiarazione;
  • Il secondo con valore 5, perché eseguiamo questa riga di log soltanto dopo aver invocato la nostra funzione sum che si occupa di aggiornare la variabile result con il risultato della somma appena eseguita; difatti, 2 + 3 ha come risultato proprio 5 😄

Noterai inoltre che in questo caso ho utilizzato la keyword let per dichiarare la mia variabile, e successivamente tramite la funzione sum sono andato ad aggiornare il valore contenuto in essa con il risultato dell’addizione.

Tuttavia, se ti riproponessi lo stesso identico esempio ma utilizzando la keyword const, le cose cambierebbero non poco:

Stesso esempio di prima, ma in salsa const (e simpatico errore annesso)

Come vedi, in questo caso Javascript ci fà notare (nemmeno troppo velatamente) che stiamo tentando di aggiornare il valore contenuto all’interno della costante result, ma essendo essa appunto una costante, ciò non è permesso, dunque viene sollevata un’eccezione (si, gli “errori” si chiamano così 🤓).

Tiriamo le somme

Dopo aver visto lo scope (o contesto di esecuzione), la dichiarazione delle variabili e le relative keyword, quello che mi sento di consigliarvi è ciò che segue:

Imparate a lavorare SOLO con let e const; la keyword var è ormai obsoleta e porta più problemi che vantaggi, salvo casi davvero specifici.

La keyword let deve essere il vostro default, il vostro metodo standard di dichiarare le variabili; quando poi avrete bisogno di creare una variabile che mantenga costante nel tempo il proprio valore, è lì che userete la keyword const.

Nella prossima lezione andremo ad analizzare i tipi di dato che Javascript ci mette a disposizione.

A presto 👋