Membri Privati e Protetti delle Classi JavaScript
Scopri i membri privati con prefisso # e la convenzione _ per i membri protetti nelle classi JavaScript, con getter, setter e best practice.
Una classe spesso ha bisogno di nascondere alcune parti del suo funzionamento interno, in modo che il codice esterno non possa leggerle o modificarle direttamente. Questa pagina illustra i due modi che JavaScript offre per farlo: i membri veramente privati dichiarati con il prefisso #, e la convenzione del trattino basso _ che gli sviluppatori usano per segnalare che un membro è "protetto" (destinato solo alla classe e alle sue sottoclassi).
Vedrai quando usare ciascuno dei due approcci, perché i campi # sono genuinamente inaccessibili dall'esterno, e come combinare i campi privati con i getter e setter per esporre un'interfaccia controllata e validata. Se le classi sono nuove per te, inizia prima con il capitolo sulla sintassi base delle classi.
Introduzione all'Incapsulamento in JavaScript
L'incapsulamento è un principio fondamentale della programmazione orientata agli oggetti (OOP): raggruppa i dati (variabili) e i metodi (funzioni) che operano su tali dati in un'unica unità — un oggetto — e controlla l'accesso agli elementi interni di quell'oggetto. L'obiettivo è esporre un'interfaccia pubblica piccola e deliberata, nascondendo i dettagli implementativi dietro di essa.
Perché è importante? Quando lo stato interno è nascosto, il codice esterno non può portare l'oggetto in uno stato non valido, e sei libero di modificare il funzionamento interno della classe senza rompere il codice che la utilizza. In JavaScript, l'incapsulamento si ottiene con campi e metodi privati #. JavaScript non ha un modificatore protected nativo, quindi gli sviluppatori lo simulano con una convenzione di denominazione (il prefisso _), come spiegato di seguito.
Proprietà e Metodi Privati
Un membro privato di una classe viene dichiarato con il prefisso # nel nome. Può essere accessibile solo dall'interno del corpo della classe — qualsiasi tentativo di leggerlo o scriverlo dall'esterno è un errore di sintassi rigido, non semplicemente undefined. Questa è una vera privacy imposta dal linguaggio.
Un campo deve essere dichiarato nel corpo della classe prima di poter essere utilizzato (non è possibile creare un campo # al volo all'interno del costruttore come si può fare con una proprietà pubblica). Si noti inoltre che i campi privati # sono non-enumerabili: non compaiono in Object.keys(), in for...in, né nell'output di JSON.stringify().
Perché # è veramente privato
A differenza della convenzione del trattino basso, un campo # è invisibile all'esterno della classe. Non è possibile accedervi tramite user.#name, tramite l'accesso con parentesi quadre come user["#name"], né tramite Object.keys(). La prima forma è un errore di sintassi; le altre semplicemente non trovano il campo.
Metodi Privati
Anche i metodi possono essere privati: basta aggiungere il prefisso # al nome del metodo. Un metodo privato è utile per le funzioni di supporto interne che i chiamanti non dovrebbero mai invocare direttamente — ad esempio, la logica di validazione o formattazione che supporta l'API pubblica ma non ne fa parte.
Proprietà e Metodi Protetti (la convenzione _)
JavaScript non ha la parola chiave protected. Per convenzione, un membro destinato ad essere usato dalla classe e dalle sue sottoclassi — ma non dal codice esterno — viene prefissato con un singolo trattino basso _. Questo è puramente un segnale per gli altri sviluppatori; un membro con _ rimane completamente leggibile e scrivibile da qualsiasi punto. Sceglilo quando le sottoclassi necessitano dell'accesso (un campo # non è accessibile dalle sottoclassi), accettando che la protezione sia per accordo, non per imposizione.
Poiché i membri con _ vengono ereditati come qualsiasi normale proprietà, una sottoclasse può fare affidamento su di essi. Questo è il motivo pratico per usare la convenzione invece di # quando si progetta per l'ereditarietà:
Incapsulamento Basato sugli Accessor: Best Practice
Quando si integrano proprietà e metodi privati e protetti nei propri progetti JavaScript, è utile seguire le seguenti best practice per massimizzarne i benefici:
- Usa i campi privati per i dati sensibili o invarianti: Memorizza tutto ciò che non deve essere toccato direttamente — contatori interni, valori memorizzati nella cache, stato grezzo — come campo
#. Questo garantisce l'integrità e previene effetti collaterali indesiderati. - Sfrutta i getter e setter: Mantieni il campo privato ed esponilo tramite un getter e setter. Il setter è l'occasione per validare o trasformare l'input prima che raggiunga il campo privato, in modo che l'oggetto non possa mai contenere un valore non valido.
Nell'esempio seguente, #age può essere modificato solo tramite il setter, che rifiuta i numeri negativi:
Puoi anche esporre uno stato di sola lettura definendo un getter senza il corrispondente setter — il consumatore può leggere il valore ma non ha modo di sovrascrivere il campo privato sottostante.
- Usa i membri protetti per l'ereditarietà: Usa proprietà e metodi protetti quando intendi renderli accessibili nelle sottoclassi. Questa strategia favorisce una struttura più flessibile e gerarchica nelle tue applicazioni.
Tecniche e Pattern Avanzati
Oltre alle basi, JavaScript consente pattern e tecniche sofisticate per incapsulare e strutturare ulteriormente il codice in modo efficace:
- Pattern modulo: Usa le closure e le espressioni di funzione immediatamente invocate (IIFE) per creare ambiti privati.
- Factory function: Queste funzioni restituiscono nuovi oggetti, permettendo dati privati tramite closure, senza la necessità della parola chiave
new. - Proxy: I Proxy JavaScript possono essere usati per creare wrapper protettivi attorno agli oggetti, controllando l'accesso alle loro proprietà e metodi.
Conclusione
Usa i campi e i metodi # quando vuoi una privacy genuina e imposta che nessun codice esterno possa raggiungere. Usa la convenzione del trattino basso _ quando le sottoclassi necessitano dell'accesso e la protezione può essere per accordo. Combina i campi privati con getter e setter per esporre un'interfaccia controllata e validata — e getter di sola lettura quando lo stato deve essere osservabile ma non modificabile.
Argomenti correlati
- Sintassi base delle classi — dichiarazione di classi, costruttori e metodi.
- Getter e setter delle proprietà — il pattern accessor utilizzato in questa pagina.
- Proprietà e metodi statici — membri che appartengono alla classe stessa, inclusi i campi statici privati.