In alcuni degli esempi che abbiamo presentato sono state dichiarate delle variabili all'interno del corpo di una funzione. Queste variabili sono accessibili soltanto all'interno della funzione e non vengono viste fuori di essa o, in termini tecnici, hanno uno scope locale.
Lo scope o ambito di visibilità di una variabile è la parte di uno script all'interno del quale si può fare riferimento ad essa. Le variabili dichiarate all'interno di una funzione sono dette locali alla funzione dal momento che sono accessibili soltanto all'interno del suo corpo.
Le variabili dichiarate fuori da qualsiasi funzione sono dette globali e sono accessibili da qualsiasi punto dello script, anche all'interno di funzioni.
Chiariamo il concetto con un esempio:
var x = 10;
var y;
function incrementa(){
var z = 5;
x = x + z;
}
incrementa();
y = x + 1;
Nell'esempio abbiamo dichiarato due variabili globali x
e y
. Entrambe sono accessibili anche all'interno della funzione incrementa()
e infatti la funzione accede a x
per incrementarla del valore della variabile locale z
.
Il valore della variabile x
dopo l'invocazione della funzione incrementa()
sarà pertanto 15
ed il valore finale assegnato a y
sarà 16
.
Variabili locali e globali possono avere lo stesso nome, ma in questo caso si vengono a creare delle ambiguità la cui risoluzione dipende dalla regola secondo cui si fa riferimento all'ambito di visibilità più vicino all'utilizzo della variabile. Quindi, se consideriamo il seguente esempio:
var x = 10;
var y;
function incrementa() {
var x = 5;
x = x + 1;
return x;
}
incrementa();
y = x + 1;
vediamo che all'interno del corpo della funzione incrementa()
viene dichiarata una variabile locale con lo stesso nome della variabile globale x
. In questo caso il riferimento a x
all'interno della funzione è inteso come il riferimento alla variabile locale, in quanto lo scope locale è il più vicino all'uso della variabile. Spesso si dice che la variabile locale nasconde la variabile globale.
Facciamo notare che, indipendentemente dal punto in cui viene dichiarata una variabile, essa esiste in tutto lo scope a cui appartiene. Consideriamo ad esempio il seguente codice:
var x = 10;
var y;
function incrementa() {
var s = x; // x ha valore undefined
var x = 5; // x ha valore 5
x = x + s; // x ha valore NaN
return x;
}
incrementa();
y = x + 1; // x ha valore 10
All'interno della funzione incrementa()
abbiamo utilizzato la variabile x
prima della sua dichiarazione. Nonostante ciò x
non fa riferimento alla variabile globale, ma a quella locale dichiarata subito dopo e il suo valore è ancora undefined
.
Un'altra osservazione, legata in qualche modo a questa appena accennata, è che a differenza di quanto accade in altri linguaggi come ad esempio C e C#:
dichiarare una variabile all'interno di un blocco di codice non crea un nuovo scope per la variabile!
In altre parole, nel seguente esempio la variabile y
è accessibile anche al di fuori del blocco associato all'istruzione if
:
function rendiPari(x) {
if (x%2 != 0) {
var y;
y= x + 1;
} else {
y = x;
}
return y;
}
Per evitare ambiguità, quindi, è opportuno dichiarare le variabili all'inizio del proprio scope, ad esempio all'inizio del corpo di una funzione, in modo evidenziare quali variabili saranno disponibili.
Let
Se invece abbiamo bisogno di creare uno scope specifico per una o più variabili possiamo ricorrere all'istruzione let. Questa istruzione, definita dalle specifiche di ECMAScript 6, consente di dichiarare una o più variabili in modo analogo a var
, ma a differenza di quest'ultima limita lo scope della variabile al blocco di codice, all'istruzione o all'espressione in cui viene utilizzata. Quindi, ad esempio, nel seguente codice:
var x = 10;
var y;
{
let x = 20;
y = x + 1;
}
y = x + y;
avremo uno scope a livello di blocco di codice in cui la variabile x
dichiarata tramite let
nasconde quella esterna dichiarata con var
. Il risultato finale combina i valori delle due variabili omonime.
Un ambito in cui è senz'altro utile utilizzare l'istruzione let
è nelle iterazioni, come ad esempio nel for
:
var x = 0;
for (let i = 0; i < 10; i++) {
x = x + 1;
}
Grazie a let
dichiariamo ed utilizziamo la variabile i
soltanto all'interno del for
, evitando eventuali collisioni con altre variabili omonime definite in scope più esterni.
Come possiamo intuire, la distinzione tra scope globale e locale delle variabili è in realtà troppo semplicistica. Come vedremo più avanti, è possibile definire una gerarchia di scope all'interno di ciascuno dei quali definire l'accessibilità delle variabili.