Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Javascript: differenze fra var, let e const

Imparare a conoscere le diverse parole chiave che permettono di dichiarare variabili su Javascript: tutte le differenze tra var, let e const.
Imparare a conoscere le diverse parole chiave che permettono di dichiarare variabili su Javascript: tutte le differenze tra var, let e const.
Link copiato negli appunti

Gli sviluppatori Javascript di più vecchia generazione sono abituati a dichiarare tutte le variabili dei propri script attraverso la parola chiave var, poiché questa è stata l'unica possibilità fino all'avvento della specifica ECMA6.

Questa nuova specifica ha infatti introdotto anche le keyword let e const, che fungono da aggiornamenti ed estensioni del mero concetto di var. Ma quali sono le reali e concrete differenze tra questi identificatori, e perché dovremmo utilizzarli nei nostri script? In questo articolo faremo completa chiarezza su questa importante faccenda.

Consideriamo il seguente snippet:

var x = 10;
var y = 20;
var z = 30;

In questo modo vengono dichiarate 3 variabili (x, y e z) alle quali vengono immediatamente assegnati dei valori. C'è però uno step in più da conoscere: le variabili dichiarate in questo modo sono considerate come variabili globali, o meglio, appartenenti allo scope globale.

Lo scope, infatti, è la risposta al perché sono stati creati altri identificatori per costruire variabili, domanda che uno sviluppatore meno esperto potrebbe, a ragione, porsi.

Scope globale e locale

In Javascript, una variabile dichiarata con la parola chiave var appartiene all'oggetto globale window, ed è accessibile anche nel seguente modo:

var x = 10;
var p = document.getElementById('demo');
p.innerHTML = window.x;

Utilizzare x o window.x è la stessa cosa. Una variabile globale può anche essere dichiarata ed assegnata bypassando la keyword var, divenendo anch'essa disponibile come proprietà dell'oggetto window:

y = 20;
var p = document.getElementById('demo');
p.innerHTML = window.y;

Questa particolare gerarchia ad oggetti è fondamentale per realizzare script più avanzati, come ad esempio accedere a variabili globali utilizzando stringhe come identificatori.

Ora, sappiamo che come in tutti i linguaggi di programmazione, anche in Javascript possiamo dichiarare ed utilizzare le funzioni. Nelle funzioni, però, esiste uno scope locale, che è differente dallo scope globale. Vediamo il seguente esempio:

var x = "fuori";
function faiQualcosa() {
var x = "dentro";
console.log(x);
}
faiQualcosa(); // dentro
console.log(x); // fuori

Come possiamo vedere, dichiariamo tramite la keyword var la variabile x sia all'esterno della funzione, nell'oggetto window, sia all'interno della funzione faiQualcosa. In questo caso Javascript non ha problemi ad interpretare le due variabili, ed infatti quella globale non viene alterata.

Le cose cambiano se all'interno della funzione assegniamo un valore x senza dichiarare la variabile con la keyword var:

var x = "fuori";
function faiQualcosa() {
x = "dentro";
console.log(x);
}
faiQualcosa(); // dentro
console.log(x); // dentro

Questo accade perchè in questo caso non abbiamo specificato che la variabile x deve avere uno scope locale. Dato che le variabili globali sono accessibili dall'interno delle funzioni, il valore di x viene alterato. Il contrario non è però vero: le variabili dichiarate nelle funzioni, quindi aventi scope locale, NON sono accessibili dall'esterno della funzione.

Possiamo dunque dire che le variabili dichiarate con var hanno uno scope locale/globale a seconda del fatto che sono dichiarate all'interno o all'esterno delle funzioni.

Block scope

La specifica ECMA2015 ha introdotto il block scope. Un "blocco" è definito attraverso le parentesi graffe { e } e contiene porzioni di codice che sono "a se" rispetto al codice globale.

La differenza più grande ed evidente tra var e let sta proprio nello scope. Una variabile dichiarata con var, NON ha block scope. Questo significa che una variabile dichiarata in un blocco utilizzando var è accessibile anche all'esterno del blocco:

// codice globale
// ...
// blocco
{
var x = 2;
}
// x è disponibile

Dunque il seguente codice:
var i = 0;
if (true) {
var i = 1;
}
console.log(i); // 1

Riscrive il valore della variabile i, che sarà modificato anche all'esterno del blocco. Questo comportamento non è sempre desiderato, specialmente nello stile di programmazione Javascript moderno, in cui il block scope può creare incongruenze.

Per questo motivo è stato creato l'identificatore let, che a differenza del precedente, HA block scope. Dunque, uno snippet identico al precedente, ma utilizzando let, produce il seguente risultato:

let i = 0;
if (true) {
let i = 1;
}
console.log(i); // 0

Sebbene la variabile i viene riassegnata all'interno del blocco if, non è affatto un problema utilizzando l'operatore let, perchè quell'assegnamento è locale e riferito al blocco di codice di appartenenza, mentre quella esterna si riferisce al blocco di codice globale.

La regola generale, utilizzando let, è descritta dal seguente snippet:

{
let x = 2;
}
// x NON è accessibile

JavaScript let VS var

Vediamo le differenze tra l'uso di var e let, racchiuse in questa analisi.

Block scope

var x = 10;
// qui x è 10
{
var x = 2;
// Qui x è 2
}
// Qui x è 2

var x = 10;
// qui x è 10
{
let x = 2;
// qui x è 2
}
// qui x è 10

La differenza è abissale. Nel primo caso, non avendo block scope, la variabile x viene sovrascritta. Nel secondo caso, la variabile x globale non viene assolutamente toccata dalla dichiarazione della variabile x locale per il blocco di codice in cui viene definita attraverso let.

Con un po' di pratica ed una buona maestria nella scrittura di Javascript moderno, utilizzare let in combinazione con var diventerà sempre più intuitivo e semplice.

Loop scope

La stessa funzionalità appena vista è attiva anche nello scope dei loop, come il for:

var i = 5;
for (var i = 0; i < 10; i++) {
// operazioni
}
// Qui i è 10

Se invece utilizziamo let:

let i = 5;
for (let i = 0; i < 10; i++) {
// operazioni
}
// Qui i è 5

Nel primo caso, usando var, il loop altera il valore della variabile i globale. Nel secondo caso, utilizzando let, abbiamo il loop scope, dunque la variabile globale non viene toccata.

Function scope

var e let hanno entrambi uno scope locale per quanto riguarda la dichiarazione nelle funzioni:

function myFunction() {
var myName = "Riccardo"; // Function Scope
}
function myOtherFunction() {
let myName = "Riccardo";   // Function Scope
}

Scope globale

Stessa cosa per quanto riguarda lo scope globale:

var x = 2; // Global scope
let y = 2;       // Global scope

Tuttavia, qui esiste una differenza di cui essere consapevoli. Come abbiamo detto in precedenza, le variabili globali dichiarate con var sono accessibili tramite l'oggetto globale window. Lo stesso tuttavia non si può dire per le variabili dichiarate con let, che NON sono accessibili tramite l'oggetto window:

var myName = "Riccardo";
var p = document.getElementById('demo');
p.innerHTML = window.myName; // okay
let myName = "Riccardo";
var p = document.getElementById('demo');
p.innerHTML = window.myName; // errore

Ridichiarazione

Ridichiarare una variabile var utilizzando l'identificatore var è consentito (ovunque):

var x = 2;
// ora x è 2
var x = 3;
// ora x è 3

Ridichiarare una variabile let utilizzando l'identificatore let in uno scope differente (come quello di blocco) è consentito:
let x = 2; // consentito
{
let x = 3; // consentito
}
{
let x = 4; // consentito
}

Ridichiarare una variabile var utilizzando l'identificatore let, nello scope globale o in uno scope di blocco NON è consentito:

var x = 2; // consentito
let x = 3; // Non consentito
{
var x = 4; // consentito
let x = 5 // Non consentito
}

Ridichiarare una variabile let utilizzando l'identificatore let, nello scope globale o in uno scope di blocco NON è consentito:

let x = 2; // consentito
let x = 3; // Non consentito
{
let x = 4; // consentito
let x = 5; // Non consentito
}

Ridichiarare una variabile let utilizzando l'identificatore var, nello scope globale o in uno scope di blocco, NON è consentito:

let x = 2; // consentito
var x = 3; // Non consentito
{
let x = 4; // consentito
var x = 5; // Non consentito
}

Hoisting

Con il termine hoisting si identifica il comporamento di Javascript in cui le variabili potevano essere utilizzate PRIMA della propria dichiarazione, dato che le dichiarazioni delle varibili, anche se collocate ad esempio alla fine dello script, venivano "spinte" sopra, e dunque la procedura era perfettamente legale. Con l'introduzione dell'indentificatore let tuttavia, questo non è più vero.

Con la keyoword var, l'hoisting è perfettamente legale:

// qui puoi usare la variabile myName anche se la sua dichiarazione
// viene svolta in seguito
myName = "Riccardo";
var p = document.getElementById('demo');
p.innerHTML = myName; // okay
// dichiarazione
var myName;

Con la keyword let, l'hoisting non è legale e risulterà in un ReferenceError, dato che a differenza delle variabili var che sono inizializzate come undefined, le variabili let non sono inizializzate:

// qui NON puoi usare la variabile myName se la sua dichiarazione
// viene svolta in seguito
var p = document.getElementById('demo');
p.innerHTML = myName; // ReferenceError
// dichiarazione
let myName;

JavaScript const

Vediamo ora il terzo ed ultimo identificatore (la seconda novità di ECMA 6 in fatto di identificatori di variabili) che è possibile utilizzare per la dichiarazione di variabili.

Le variabili dichiarate con const si comportano in maniera simile a quelle dichiarate con let, con la differenza che le prime non possono essere riassegnate. Questo elimina completamente la possibilità di sovrascrivere le variabili const. Ad esempio:

const PI = 3.141592653589793;
PI = 3.14; // errore
PI = PI + 10; // errore

Anche le variabili const risentono del block scope, in maniera indentica a quelle let. Il seguente snippet è perfettamente legale:

var x = 10;
// qui x è 10
{
const x = 2;
// qui x è 2
}
// qui x è 10

Un'altra particolarità delle variabili const risiede nel fatto che devono necessariemente essere assegnate in fase di dichiarazione. La seguente riga di codice è valida:

const PI = 3.14159265359;

Mentre la seguente produrrà un errore di tipo SyntaxError:

const PI;
PI = 3.14159265359;
// Uncaught SyntaxError: Missing initializer in const declaration

In questo caso le variabili const possono apparire come delle vere costanti, dato che non possono nemmeno essere dichiarate e assegnate in seguito (come una variabile):

const i;
// Uncaught SyntaxError: Missing initializer in const declaration

Tuttavia, questa caratteristica ha una lieve eccezione, che riguarda, nel caso di tipi di dato non primitivi (ovvero array e oggetti) i valori, le proprietà ed i metodi delle variabili. Se non è comunque possibile riassegnare il tipo di dato in una variabile const, è possibile però riassegnare un suo elemento:
// creazione di un oggetto
const car = {type:"Fiat", model:"500", color:"white"};
// legale: aggiornare proprietà
car.color = "red";
// legale: aggiungere proprietà
car.owner = "Johnson";

Tuttavia non è possibile aggiornare la variabile const tramite riassegnazione:

const car = {type:"Fiat", model:"500", color:"white"};
car = {type:"Volvo", model:"EX60", color:"red"}; // ERRORE

Questo snippet genera un SyntaxError: Uncaught SyntaxError: Identifier 'car' has already been declared.

La stessa identica cosa accade con gli array. È possibile modificare i valori di una variabile const array, ma non riassegnarla:

const cars = ["Saab", "Volvo", "BMW"];
// legale: aggiornamento di proprietà
cars[0] = "Toyota";
// legale: aggiunta di un elemento
cars.push("Audi");
// illegale
cars = ["Toyota", "Volvo", "Audi"];    // ERRORE

Conclusione

Come abbiamo potuto vedere, abbiamo due nuovi e potenti strumenti a nostra disposizione, che ci permettono di scrivere codice Javascript in una maniera più intuitiva, chiara e concisa. A differenza di alcuni sviluppatori che sostengono che let e const debbano sostiuire completamente var, la mia opinione è che occorre utilizzare tutti e tre gli identificatori nelle situazioni corrette, conoscendo alla perfezione i loro comportamenti e le loro caratteristiche. L'identificatore var resta molto importante anche per ragioni di retrocompatibilità.

Ti consigliamo anche