JavaScript utilizza l’ereditarietà prototipale: ogni oggetto eredita proprietà e metodi dal suo oggetto prototipo.
La classe tradizionale come progetto per creare oggetti, utilizzata in linguaggi come Java o Swift, non esiste in JavaScript. L’ereditarietà prototipale riguarda solo gli oggetti.
L’ereditarietà prototipale può emulare l’ereditarietà classica della classe. Per portare le classi tradizionali a JavaScript, lo standard ES2015 introduce la sintassiclass
: uno zucchero sintattico sull’eredità prototipale.,
Questo post ti familiarizza con le classi JavaScript: come definire una classe, inizializzare l’istanza, definire campi e metodi, comprendere i campi privati e pubblici, cogliere i campi e i metodi statici.
1. Definizione: la parola chiave class
La parola chiave speciale class
definisce una classe JavaScript:
class User { // The body of class}
Il codice definisce una classe User
. Le parentesi graffe { }
delimitano il corpo della classe. Si noti che questa sintassi è denominata dichiarazione di classe.,
Non sei obbligato a indicare il nome della classe. Utilizzando un’espressione di classe è possibile assegnare la classe a una variabile:
const UserClass = class { // The body of class};
È possibile esportare facilmente una classe come parte di un modulo ES2015. Ecco la sintassi per un’esportazione predefinita:
export default class User { // The body of class}
E un’esportazione denominata:
export class User { // The body of class}
La classe diventa utile quando si crea un’istanza della classe. Un’istanza è un oggetto contenente dati e comportamenti descritti dalla classe.,
L’operatore new
istanzia la classe in JavaScript: instance = new Class()
.
Per esempio, è possibile creare un’istanza User
classe utilizzando il new
operatore:
const myUser = new User();
new User()
crea un’istanza del User
classe.
2. Inizializzazione: constructor ()
constructor(param1, param2, ...)
è un metodo speciale nel corpo di una classe che inizializza l’istanza., Questo è il luogo in cui si impostano i valori iniziali per i campi o si esegue qualsiasi tipo di configurazione dell’oggetto.
Nell’esempio seguente, il costruttore imposta il valore iniziale del campo name
:
class User { constructor(name) { this.name = name; }}
User
’s costruttore con un parametro name
che viene utilizzato per impostare il valore iniziale del campo this.name
.
All’interno del costruttorethis
il valore è uguale all’istanza appena creata.,
Gli argomenti utilizzati per istanziare la classe diventano i parametri del costruttore:
class User { constructor(name) { name; // => 'Jon Snow' this.name = name; }}const user = new User('Jon Snow');
name
il parametro all’interno del costruttore ha il valore'Jon Snow'
.
Se non si definisce un costruttore per la classe, ne viene creato uno predefinito. Il costruttore predefinito è una funzione vuota, che non modifica l’istanza.
Allo stesso tempo, una classe JavaScript può avere fino a un costruttore.
3. Campi
I campi di classe sono variabili che contengono informazioni., I campi possono essere collegati a 2 entità:
- Campi di istanza di classe
- Campi della classe stessa (aka statico)
inoltre, i campi di avere 2 livelli di accessibilità:
- Pubblico: il campo è accessibile ovunque
- Privato: il campo è accessibile solo all’interno del corpo della classe
3.1 Pubblico campi di istanza
vediamo di nuovo il precedente frammento di codice:
class User { constructor(name) { this.name = name; }}
L’espressione this.name = name
crea un’istanza del campo name
e gli assegna un valore iniziale.,
In seguito è possibile accedere a name
campo utilizzando un accessor proprietà:
const user = new User('Jon Snow');user.name; // => 'Jon Snow'
name
è un campo pubblico perché è possibile accedervi al di fuori del User
corpo di classe.
Quando i campi vengono creati implicitamente all’interno del costruttore, come nello scenario precedente, potrebbe essere difficile cogliere l’elenco dei campi. Devi decifrarli dal codice del costruttore.
Un approccio migliore è quello di dichiarare esplicitamente i campi della classe., Indipendentemente dal costruttore, l’istanza ha sempre lo stesso insieme di campi.
La proposta campi classe consente di definire i campi all’interno del corpo della classe. Inoltre, è possibile indicare il valore iniziale subito:
class SomeClass { field1; field2 = 'Initial value'; // ...}
andiamo a modificare il User
classe e dichiarare un campo pubblico name
:
class User { name; constructor(name) { this.name = name; }}const user = new User('Jon Snow');user.name; // => 'Jon Snow'
name;
all’interno del corpo della classe dichiara un campo pubblico name
.,
I campi pubblici dichiarati in questo modo sono espressivi: una rapida occhiata alle dichiarazioni dei campi è sufficiente per comprendere la struttura dei dati della classe.
Inoltre, il campo della classe può essere inizializzato immediatamente alla dichiarazione.
class User { name = 'Unknown'; constructor() { // No initialization }}const user = new User();user.name; // => 'Unknown'
name = 'Unknown'
all’interno del corpo della classe dichiara un camponame
e lo inizializza con il valore'Unknown'
.
Non ci sono restrizioni sull’accesso o l’aggiornamento dei campi pubblici., È possibile leggere e assegnare valori ai campi pubblici all’interno del costruttore, dei metodi e all’esterno della classe.
3.2 Campi di istanza privati
L’incapsulamento è un concetto importante che consente di nascondere i dettagli interni di una classe. Qualcuno che utilizza una classe incapsulata dipende solo dall’interfaccia pubblica fornita dalla classe e non si accoppia ai dettagli di implementazione della classe.
Le classi organizzate pensando all’incapsulamento sono più facili da aggiornare quando cambiano i dettagli dell’implementazione.,
Un buon modo per nascondere i dati interni di un oggetto è usare i campi privati. Questi sono i campi che possono essere letti e modificati solo all’interno della classe a cui appartengono. Il mondo esterno della classe non può modificare direttamente i campi privati.
I campi privati sono accessibili solo all’interno del corpo della classe.
Prefisso il nome del campo con il simbolo speciale #
per renderlo privato, ad esempio #myField
., Il prefisso #
deve essere mantenuto ogni volta che si lavora con il campo: dichiararlo, leggerlo o modificarlo.
Assicuriamoci che il campo#name
possa essere impostato una volta all’inizializzazione dell’istanza:
#name
sia un campo privato. È possibile accedere e modificare #name
all’interno del corpo del User
. Il metodo getName()
(maggiori informazioni sui metodi nella sezione successiva) può accedere al campo privato #name
.,
Ma se si tenta di accedere al campo privato#name
al di fuori delUser
corpo della classe, viene generato un errore di sintassi:SyntaxError: Private field '#name' must be declared in an enclosing class
.
3.3 Campi statici pubblici
È anche possibile definire i campi sulla classe stessa: i campi statici. Questi sono utili per definire le costanti di classe o memorizzare informazioni specifiche per la classe.
Per creare campi statici in una classe JavaScript, utilizzare la parola chiave speciale static
seguita dal nome del campo: static myStaticField
.,
Aggiungiamo un nuovo campotype
che indica il tipo di utente: admin o regular. I campi statici TYPE_ADMIN
e TYPE_REGULAR
sono a portata di mano costanti per differenziare i tipi di utente:
static TYPE_ADMIN
e static TYPE_REGULAR
definire le variabili statiche all’interno del User
classe. Per accedere ai campi statici, è necessario utilizzare la classe seguita dal nome del campo: User.TYPE_ADMIN
eUser.TYPE_REGULAR
.
3.,4 Campi statici privati
A volte anche i campi statici sono un dettaglio di implementazione che desideri nascondere. A questo proposito, è possibile rendere privati i campi statici.
Per rendere privato il campo statico, prefisso il nome del campo con #
simbolo speciale: static #myPrivateStaticField
.
Supponiamo che desideri limitare il numero di istanze della classeUser
., Per nascondere i dettagli sui limiti delle istanze, è possibile creare i campi statici privati:
Il campo staticoUser.#MAX_INSTANCES
imposta il numero massimo di istanze consentite, mentre il campo staticoUser.#instances
conta il numero effettivo di istanze.
Questi campi statici privati sono accessibili solo all’interno della classeUser
. Nulla dal mondo esterno può interferire con il meccanismo dei limiti: questo è il vantaggio dell’incapsulamento.
4. Metodi
I campi contengono dati., Ma la possibilità di modificare i dati viene eseguita da funzioni speciali che fanno parte della classe: i metodi.
Le classi JavaScript supportano sia l’istanza che i metodi statici.
4.1 Metodi di istanza
I metodi di istanza possono accedere e modificare i dati di istanza. I metodi di istanza possono chiamare altri metodi di istanza, nonché qualsiasi metodo statico.,
Ad esempio, definiamo un metodogetName()
che restituisce il nome nella classeUser
:
In un metodo di classe, così come nel costruttore,this
valore uguale all’istanza di classe. Utilizzare this
per accedere ai dati dell’istanza: this.field
, o anche chiamare altri metodi: this.method()
.,
aggiungiamo un nuovo metodo nameContains(str)
che ha un parametro e chiama un altro metodo:
nameContains(str) { ... }
è un metodo di User
classe che accetta un parametro str
. Inoltre, esegue un altro metodo dell’istanzathis.getName()
per ottenere il nome dell’utente.
Un metodo può anche essere privato. Per rendere il metodo privato prefisso il suo nome con #
.
FacciamogetName()
metodo privato:
#getName()
è un metodo privato., All’interno del metodo nameContains(str)
si chiama un metodo privato in questo modo: this.#getName()
.
Essendo privato,#getName()
non può essere chiamato al di fuori delUser
corpo della classe.
4.2 Getter e setter
Il getter e il setter imitano il campo normale, ma con un maggiore controllo su come il campo è accessibile e modificato.
Il getter viene eseguito su un tentativo di ottenere il valore del campo, mentre setter su un tentativo di impostare un valore.,
assicurarsi che il name
proprietà User
non può essere vuoto, lascia avvolgere il campo privato #nameValue
in un getter e setter:
get name() {...}
getter viene eseguito quando si accede il valore del campo: user.name
.
4.3 Metodi statici
I metodi statici sono funzioni collegate direttamente alla classe. Tengono la logica relativa alla classe, piuttosto che all’istanza della classe.,
Per creare un metodo statico utilizzare la parola chiave specialestatic
seguita da una sintassi del metodo regolare:static myStaticMethod() { ... }
.
Quando si lavora con metodi statici, ci sono 2 semplici regole da ricordare:
- Un metodo statico può accedere ai campi statici
- Un metodo statico non può accedere ai campi di istanza.
Ad esempio, creiamo un metodo statico che rileva se un utente con un nome specifico è già stato preso.,
isNameTaken()
è un metodo statico che utilizza il campo privato statico User.#takenNames
per verificare la presenza di nomi presi.
5. Ereditarietà: estende
Le classi in JavaScript supportano l’ereditarietà singola utilizzando la parola chiaveextends
.
Nell’espressioneclass Child extends Parent { }
la classeChild
eredita daParent
il costruttore, i campi e i metodi.
Ad esempio, creiamo una nuova classe figlioContentWriter
che estende la classe padreUser
.,
ContentWriter
eredita dalUser
il costruttore, il metodogetName()
e il campo name
. Inoltre, la classe ContentWriter
dichiara un nuovo campo posts
.
Si noti che i membri privati di una classe padre non sono ereditati dalla classe figlio.
5.1 Parent constructor: super() in constructor ()
Se si desidera chiamare il parent constructor in una classe figlio, è necessario utilizzare la funzione specialesuper()
disponibile nel costruttore figlio.,
Per esempio, facciamo ContentWriter
chiamata al costruttore il costruttore padre di User
, così come inizializzare il post di campo:
super(name)
all’interno della classe di bambini ContentWriter
esegue il costruttore della classe padre User
.
Si noti che all’interno del costruttore figlio è necessario eseguiresuper()
prima di utilizzarethis
parola chiave. Chiamando super()
si assicura che il costruttore padre inizializzi l’istanza.,
class Child extends Parent { constructor(value1, value2) { // Does not work! this.prop2 = value2; super(value1); }}
5.2 Istanza genitore: super nei metodi
Se si desidera accedere al metodo genitore all’interno di un metodo figlio, è possibile utilizzare la scorciatoia speciale super
.
getName()
della classe figlio ContentWriter
accede al metodo super.getName()
direttamente dalla classe genitore User
.
Questa funzione è chiamata sovrascrittura del metodo.
Si noti che è possibile utilizzaresuper
anche con metodi statici, per accedere ai metodi statici del genitore.
6., Object type checking: instanceof
object instanceof Class
is the operator that determines if object
is an instance of Class
.
Let’s see instanceof
operator in action:
user
is an instance of User
class, user instanceof User
evaluates to true
.
The empty object {}
is not an instance of User
, correspondingly obj instanceof User
is false
.,
instanceof
è polimorfico: l’operatore rileva un figlio come istanza della classe genitore.
writer
è un’istanza della classe figlio ContentWriter
. L’operatore writer instanceof ContentWriter
valutatrue
.
Allo stesso tempo ContentWriter
è una classe figlio di User
. Quindi writer instanceof User
valuta anchetrue
.
Cosa succede se si desidera determinare la classe esatta dell’istanza?, È possibile utilizzare la proprietàconstructor
e confrontarla direttamente con la classe:
writer.constructor === ContentWriter; // => truewriter.constructor === User; // => false
7. Classi e prototipi
Devo dire che la sintassi della classe in JavaScript fa un ottimo lavoro per astrarre dall’ereditarietà prototipale. Per descrivere la sintassiclass
non ho nemmeno usato il termine prototipo.
Ma le classi sono costruite sopra l’ereditarietà prototipale. Ogni classe è una funzione e crea un’istanza quando viene invocata come costruttore.
I seguenti due frammenti di codice sono equivalenti.,
La versione della classe:
La versione che utilizza prototype:
La sintassi della classe è molto più semplice da lavorare se si ha familiarità con il classico meccanismo di ereditarietà dei linguaggi Java o Swift.
Ad ogni modo, anche se usi la sintassi della classe in JavaScript, ti consiglio di avere una buona conoscenza dell’ereditarietà prototipale.
8. Disponibilità delle funzionalità di classe
Le funzionalità di classe presentate in questo post sono distribuite su ES2015 e proposte nella fase 3.,
Alla fine del 2019, la funzionalità di classe sono divisi tra:
- Pubblici e privati, campi di istanza sono parte dei campi della Classe proposta
- Privato metodi di istanza e accessori sono parte di una Classe metodi privati proposta
- Pubblici e privati, campi statici e privati, i metodi statici sono parte della Classe statico funzioni di proposta
- Il resto è parte di ES2015 standard.
9. Conclusione
Le classi JavaScript inizializzano le istanze con i costruttori, definiscono campi e metodi., È possibile allegare campi e metodi anche sulla classe stessa utilizzando la parola chiave static
.
L’ereditarietà viene ottenuta utilizzandoextends
parola chiave: è possibile creare facilmente una classe figlio da un genitore. super
parola chiave viene utilizzata per accedere alla classe padre da una classe figlio.
Per sfruttare l’incapsulamento, rendi privati i campi e i metodi per nascondere i dettagli interni delle tue classi. I nomi dei campi e dei metodi privati devono iniziare con #
.,
Le classi in JavaScript diventano sempre più comode da usare.
Cosa ne pensi di usare #
per prefisso proprietà private?