I membri statici appartengono alla classe, non all'istanza
La maggior parte dei metodi che scrivi in una classe lavora su un'istanza specifica: un certo user, un certo circle. I membri statici sono diversi: appartengono alla classe stessa. Li chiami sul nome della classe, non su un'istanza.
double vive su MathUtils, non sulle sue istanze. Creare un'istanza non serve: m.double è undefined. È l'opposto dei metodi normali, che vivono sulle istanze (tecnicamente sul prototipo) e sono invisibili alla classe.
Il modello mentale: la parola chiave class crea due cose, un insieme di metodi di istanza (usati da new MathUtils()) e un insieme di metodi statici (usati direttamente da MathUtils). La parola chiave static sceglie in quale dei due finisce un membro.
L'uso classico: i metodi factory
Il motivo più comune per scrivere un metodo statico è un costruttore alternativo. Il vero constructor accetta gli argomenti che accetta, ma spesso vuoi altri modi per costruire un oggetto: da un JSON, da una riga di database, da un URL.
fromJSON non usa un utente: ne produce uno. È esattamente il caso in cui un metodo statico è adatto. L'alternativa sarebbe una funzione parseUser isolata, ma tenerla sulla classe raggruppa i comportamenti correlati e rende chiara l'intenzione nel punto in cui la chiami.
Proprietà statiche
Puoi anche collegare dei dati alla classe stessa:
Circle.PI è condiviso da tutti i cerchi: è una costante della classe, non viene copiata in ogni istanza. Dentro i metodi di istanza lo richiami tramite il nome della classe (Circle.PI), non tramite this.
Le proprietà statiche sono comode per la configurazione, per cache condivise tra istanze, per contatori e per costanti a livello di classe.
Dentro un metodo statico this è la classe
Dentro un metodo normale this è l'istanza. Dentro un metodo statico this è la classe:
this.count dentro increment significa Counter.count. All'inizio può sembrare strano, ma è ciò che fa funzionare l'ereditarietà dei metodi statici: this si riferisce alla classe su cui hai chiamato il metodo, non a quella che lo ha definito.
Metodi statici ed ereditarietà
I metodi statici vengono ereditati dalle sottoclassi. E poiché this punta alla classe su cui è stato chiamato il metodo, i metodi factory producono automaticamente la sottoclasse giusta:
Animal.create usa new this(name). Quando chiami Dog.create("Rex"), this è Dog, quindi new this(name) costruisce un Dog. Se lì scrivessi new Animal(name) otterresti sempre un Animal, e il pattern si romperebbe. È il motivo principale per cui this nei metodi statici punta alla classe.
Static vs istanza: un confronto pratico
Ecco lo stesso tipo di logica divisa tra i due stili:
Entrambi fanno lo stesso calcolo. La versione di istanza legge i dati da this.celsius. La versione statica riceve l'input come argomento. Scegli il metodo di istanza quando l'operazione è "qualcosa che fa questo oggetto"; scegli il metodo statico quando è "qualcosa che questa classe sa calcolare, dati certi input".
Blocchi statici per l'inizializzazione
A volte inizializzare una proprietà statica richiede più di una singola espressione: un ciclo, una condizione, più valori che dipendono l'uno dall'altro. Per questo esistono i blocchi statici:
Un blocco static { ... } viene eseguito una sola volta, quando la classe viene definita. Al suo interno this è la classe. Usalo per un'inizializzazione in più passaggi; per un singolo assegnamento è più chiaro un semplice campo statico.
Membri statici privati
Anche i campi statici possono essere privati, con il prefisso #. Sono accessibili solo dall'interno della classe:
#nextId è sigillato dentro la classe. Il codice esterno può chiamare IdGenerator.next(), ma non può toccare né azzerare il contatore. I campi privati avranno presto una pagina tutta loro, ma è utile sapere che la combinazione di static e # funziona.
Quando non usare static
I metodi statici sono un buon modo per raggruppare funzioni di supporto, ma non sono un motivo per trasformare ogni utilità in una classe. Se hai un file pieno di funzioni indipendenti, esporta le funzioni: non avvolgerle in una classe con soli metodi statici solo per dar loro un namespace. Un modulo fa già questo lavoro, e in modo più pulito.
Usa static quando:
- La funzione appartiene davvero a una classe (factory, convertitore, validatore per quel tipo).
- Ti serve uno stato condiviso tra tutte le istanze di quella classe.
- Una sottoclasse potrebbe voler sovrascrivere o ereditare il comportamento.
Altrimenti, una semplice funzione è lo strumento più semplice.
Prossimo passo: i campi privati
Hai visto di sfuggita #nextId: è la sintassi dei campi privati di JavaScript. Funziona sia con i membri di istanza sia con quelli statici, ed è il modo moderno per nascondere i dettagli di implementazione dentro una classe. È l'argomento della prossima pagina.
Domande frequenti
Cos'è un metodo statico in JavaScript?
Un metodo statico appartiene alla classe stessa, non alle sue istanze. Lo dichiari con la parola chiave static e lo chiami direttamente sulla classe: MyClass.doThing(). Le istanze non possono accedervi tramite this.doThing(): solo la classe può farlo.
Quando conviene usare un metodo statico invece di uno di istanza?
Quando la funzione è legata alla classe ma non ha bisogno di leggere o modificare un'istanza specifica. I casi tipici: metodi factory come User.fromJSON(...), funzioni di utilità come Math.max e costanti che vivono sulla classe come in un namespace. Se il metodo ha bisogno che this punti a un'istanza, allora è un metodo di istanza.
I metodi statici possono accedere alle proprietà di istanza?
Non direttamente. Dentro un metodo statico this si riferisce alla classe, non a un'istanza, quindi this.name legge una proprietà statica, non un campo di istanza. Se ti servono i dati di un'istanza, passala come argomento: static summarize(user) { return user.name; }.
I metodi statici vengono ereditati in JavaScript?
Sì. Quando una sottoclasse estende una classe padre, i metodi statici del padre sono disponibili anche sulla sottoclasse. Dentro un metodo statico this si riferisce alla classe su cui l'hai chiamato, ed è proprio questo che fa funzionare correttamente i metodi factory statici anche con le sottoclassi.