HOWTO · JavaScript

Variabili statiche in JavaScript

Questo tutorial introduce le variabili statiche in JavaScript.

In questa pagina

In JavaScript è possibile mantenere un valore tra una chiamata e l’altra usando un campo statico pubblico di una classe, una proprietà di un oggetto funzione oppure una variabile locale mantenuta da una closure. Tutti questi schemi conservano lo stato, ma differiscono per il modo in cui lo stato è associato e per chi può accedervi. Usa un campo di classe per uno stato condiviso dalla classe, una proprietà della funzione per uno stato semplice e pubblico legato a una funzione, oppure una closure quando il codice chiamante deve usare il valore senza poter leggere o modificare direttamente la variabile che lo contiene.

Cosa significa variabile statica in JavaScript

Una variabile statica è uno stato associato a una funzione o a una classe, anziché a una specifica chiamata. JavaScript non offre una dichiarazione universale di variabile statica valida per ogni funzione: mette invece a disposizione diversi schemi. La parola chiave static dichiara un campo o un metodo di classe direttamente sulla classe. Anche una proprietà assegnata a una funzione appartiene all’oggetto funzione. Una closure mantiene una variabile locale accessibile a una funzione interna anche dopo che la funzione esterna è terminata.

Questi concetti sono collegati, ma i relativi meccanismi non sono intercambiabili sotto ogni aspetto. I campi di classe e le proprietà delle funzioni sono accessibili tramite la funzione o l’oggetto classe a cui appartengono. Una closure può mantenere privata la propria variabile ed esporre solo le operazioni che scegli.

Usa un campo statico di classe per uno stato condiviso dalla classe

Un campo statico pubblico appartiene alla classe, quindi tutte le istanze fanno riferimento allo stesso campo tramite il nome della classe. Il campo non viene copiato in ogni istanza. Questa sintassi richiede un runtime che supporti i campi statici pubblici di classe. Se il runtime non li supporta, puoi usare lo schema della proprietà di funzione o quello della closure descritti qui sotto.

Esegui questo esempio:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

class VisitCounter {
  static count = 0;

  static recordVisit() {
    return ++VisitCounter.count;
  }
}

console.log(VisitCounter.recordVisit());
console.log(VisitCounter.recordVisit());
console.log(VisitCounter.count);
1
2
2

Entrambe le chiamate aggiornano lo stesso VisitCounter.count. Poiché il campo è pubblico, il codice può anche leggerlo o assegnargli un valore tramite VisitCounter.count; se vuoi che sia la classe stessa a controllarne l’accesso, usa un campo statico privato (con un nome che inizia con #). Un membro statico si usa tramite la classe, non tramite un’istanza come new VisitCounter().

Assegna una proprietà a una funzione

Le funzioni sono oggetti: una funzione con nome può quindi avere una proprietà che conserva il proprio valore tra le chiamate. È una soluzione compatta per un contatore o una cache associati a una singola funzione. La proprietà è pubblica: altro codice può leggere o modificare nextId.counter, quindi questo schema non garantisce la riservatezza.

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

function nextId() {
  nextId.counter = (nextId.counter ?? 0) + 1;
  return nextId.counter;
}

console.log(nextId());
console.log(nextId());
console.log(nextId.counter);
1
2
2

Lo stesso principio si applica a una funzione costruttrice. Una proprietà come Widget.count appartiene alla funzione costruttrice, mentre widget.name appartiene a una singola istanza. Ogni istanza non riceve una copia di Widget.count: il codice accede alla proprietà condivisa tramite Widget. Per quanto riguarda la titolarità dello stato, questo schema ricorda lo stato condiviso da una classe; tuttavia, le proprietà delle funzioni in JavaScript non offrono tutti i controlli di accesso o le garanzie sui tipi disponibili in alcuni sistemi di classi.

Usa una closure per mantenere privato lo stato

Una closure permette a una funzione interna di continuare a usare una variabile locale dopo che la funzione esterna è terminata. Una IIFE (Immediately Invoked Function Expression) crea subito quello scope esterno e restituisce un’operazione che può aggiornare il valore mantenuto. Poiché la variabile non viene inserita in un oggetto pubblico, il codice esterno non può leggerla o assegnarle direttamente un valore.

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

const counter = (function () {
  let count = 0;
  return function () {
    return ++count;
  };
})();

console.log(counter());
console.log(counter());
1
2

In questo esempio, counter mantiene un riferimento a count; ogni chiamata a counter() incrementa lo stesso valore conservato, perciò le chiamate mostrano prima 1 e poi 2. Il valore resta privato, mentre counter è l’interfaccia pubblica. Per creare uno stato indipendente, invoca una funzione factory separata che crea e restituisce una nuova closure: chiamare il counter restituito incrementa soltanto lo stato che già conserva. Se il codice chiamante deve azzerare o leggere il conteggio, restituisci un piccolo oggetto con metodi espliciti invece di esporre direttamente la variabile.

Perché evitare arguments.callee

arguments.callee fa riferimento alla funzione in esecuzione tramite il relativo oggetto arguments. Alcuni schemi più datati lo usavano per assegnare una proprietà a quella funzione, in modo simile a una proprietà di una funzione con nome. È una tecnica obsoleta e vietata in modalità strict: il codice che la usa può quindi non funzionare quando si applica tale modalità, anche nei moduli ECMAScript. Le funzioni con nome rendono più chiaro il rapporto tra la funzione e la proprietà; le closure consentono invece di mantenere privato lo stato senza ricorrere a questa proprietà limitata. Per il nuovo codice, preferisci uno degli schemi supportati descritti sopra anziché basarti su arguments.callee.