W3docs

Ereditarietà delle classi JavaScript

Impara l'ereditarietà delle classi JavaScript con extends e super: catena dei prototipi, override di metodi e proprietà, chiamata al codice padre, estensione di built-in ed ereditarietà di metodi statici.

Introduzione all'ereditarietà delle classi JavaScript

L'ereditarietà delle classi è un concetto fondamentale della programmazione orientata agli oggetti che consente a una classe di ereditare proprietà e metodi da un'altra classe. In JavaScript, l'ereditarietà delle classi viene implementata usando la parola chiave extends, che fornisce un modo per creare una classe derivata che eredita da una classe base.

Per ulteriori informazioni sulla sintassi di base, consulta JavaScript: Classi e sintassi di base.

Questo capitolo spiega come creare classi derivate, eseguire l'override di metodi e proprietà, chiamare il parent con super, estendere classi built-in come Array ed ereditare metodi statici — oltre alla catena dei prototipi che fa funzionare tutto sotto il cofano.

Come funziona l'ereditarietà: la catena dei prototipi

La parola chiave class è zucchero sintattico sull'ereditarietà prototipale di JavaScript. Quando scrivi class Circle extends Shape, il motore crea due collegamenti:

  • Circle.prototype.__proto__ === Shape.prototype — in modo che i metodi delle istanze vengano risolti risalendo la catena.
  • Circle.__proto__ === Shape — in modo che anche i metodi statici siano ereditati.

Quando leggi una proprietà o chiami un metodo su un object, il motore guarda prima all'object stesso. Se non viene trovato lì, risale la catena dei prototipi — verso Circle.prototype, poi Shape.prototype, poi Object.prototype, poi null — fermandosi alla prima corrispondenza. Questo è esattamente il motivo per cui un'istanza di Circle può chiamare un metodo definito su Shape.

javascript— editable

Object.getPrototypeOf(obj) restituisce il collegamento successivo nella catena (il modo standard e preferito per ispezionarla). L'accessor legacy obj.__proto__ punta allo stesso object. Comprendere questa catena spiega tutto ciò che segue: l'override funziona perché un prototipo più vicino oscura uno più lontano, e super funziona perché salta esplicitamente al prototipo del parent.

Creare una classe derivata

Per creare una classe che eredita da un'altra, si usa la parola chiave extends:

javascript— editable

In questo esempio, Circle estende Shape, il che significa che eredita le proprietà e i metodi di Shape, fornendo allo stesso tempo metodi propri. Si noti che Circle non definisce un proprio costruttore. In JavaScript, le classi derivate senza un costruttore esplicito chiamano automaticamente super() con gli stessi argomenti passati al costruttore della classe derivata.

Override dei metodi

Le classi derivate possono fare l'override dei metodi delle loro classi base per fornire un comportamento specifico alla sottoclasse.

javascript— editable

Qui, Circle fa l'override del metodo print per riflettere il suo tipo specifico.

Chiamare i metodi del parent con super

Puoi anche chiamare il metodo di una classe parent dall'interno della classe derivata usando super.methodName(). Questo è utile quando vuoi estendere il comportamento del parent piuttosto che sostituirlo completamente.

javascript— editable

Qui, super.print() esegue la logica del parent prima di aggiungere l'output specifico della sottoclasse.

Un override realistico: calcolo dell'area

L'override brilla quando ogni sottoclasse ha bisogno di un comportamento genuinamente diverso. Qui una Shape base definisce l'interfaccia condivisa, e ogni sottoclasse fa l'override di area() con il proprio calcolo reale. Chiamare area() su un Circle si risolve in Circle.prototype.area, che oscura la versione base.

javascript— editable

Si noti che describe() è definito solo su Shape, eppure chiama this.area() e ottiene l'implementazione della sottoclasse. Questa è la catena dei prototipi in azione: this si riferisce sempre all'istanza effettiva, quindi la ricerca del metodo parte da Circle o Rectangle.

Override delle proprietà e lettura di super.prop

super non è limitato ai metodi — puoi leggere una proprietà definita sul prototipo del parent con super.prop, il che è utile quando una sottoclasse vuole costruire su un getter del parent piuttosto che sostituirlo.

javascript— editable

Accesso al costruttore del parent: parola chiave super

Quando una classe estende un'altra classe, la funzione costruttore della classe derivata deve chiamare il costruttore del parent usando super() prima di poter usare this. Ecco come super viene usato nei costruttori per garantire che la classe parent venga inizializzata:

javascript— editable

Perché super() deve essere eseguito prima di this

In una classe derivata, l'object istanza non viene creato finché super() non viene eseguito — questo è il compito del costruttore del parent. Fino ad allora, this si trova in uno stato non inizializzato, quindi toccarlo (leggere, assegnare o anche restituire l'object implicitamente) genera un ReferenceError. Questa è una regola del linguaggio, non una preferenza di stile.

javascript— editable

Una regola correlata: se una classe derivata definisce un costruttore, deve chiamare super() da qualche parte prima di terminare, altrimenti viene generato lo stesso ReferenceError. (Una classe derivata senza un costruttore esplicito va bene — JavaScript inserisce constructor(...args) { super(...args); } automaticamente.) Una volta che super() ritorna, this è completamente inizializzato e pronto per l'uso.

Ereditarietà dei metodi statici

I membri statici appartengono alla classe stessa, non alle istanze. Poiché extends collega anche Circle.__proto__ a Shape, le classi derivate ereditano i metodi statici e possono chiamarli direttamente. Per ulteriori informazioni su come dichiararli, consulta Proprietà e metodi statici JavaScript.

javascript— editable

All'interno di un metodo statico, this si riferisce alla classe su cui è stato invocato, quindi Shape.create chiamato come Circle.create costruisce un Circle.

Ereditarietà a più livelli

Le catene possono essere più di due livelli profondi. La ricerca del metodo risale semplicemente ulteriormente la catena dei prototipi, e super si riferisce sempre al prototipo un livello sopra la classe in cui il metodo è definito.

javascript— editable

Estensione delle classi built-in

Puoi estendere classi native come Array, Error o Map per creare versioni specializzate che mantengono tutto il comportamento built-in aggiungendo il proprio.

javascript— editable

Il problema con Symbol.species. Metodi come map, filter e slice restituiscono una nuova collezione. Per impostazione predefinita restituiscono un'istanza della tua sottoclasse (MyArray), non un semplice Array — il che di solito va bene, ma può sorprendere il codice che si aspetta un vero Array. Puoi tornare agli array semplici facendo l'override del getter statico Symbol.species.

javascript— editable

Per combinare comportamenti da più sorgenti (JavaScript non ha ereditarietà multipla), consulta JavaScript Mixins.

Punti chiave:

  • Usa extends per derivare una classe; collega sia la catena dei prototipi (metodi delle istanze) che la classe stessa (metodi statici) al parent.
  • La ricerca di metodi e proprietà percorre la catena dei prototipi, fermandosi alla prima corrispondenza — ecco perché una definizione più vicina fa l'override di una più lontana. Ispeziona la catena con Object.getPrototypeOf().
  • In un costruttore derivato, chiama super() prima di this. Finché super() non viene eseguito, l'istanza non è inizializzata e qualsiasi uso di this genera un ReferenceError.
  • Accedi esplicitamente al comportamento del parent con super.method() o super.prop, anche quando l'hai fatto l'override.
  • Puoi estendere i built-in come Array — presta solo attenzione al comportamento di Symbol.species quando i metodi restituiscono nuove collezioni.

Passi successivi:

Esercitazione

Pratica
Nell'ereditarietà delle classi JavaScript, quali affermazioni sono vere?
Nell'ereditarietà delle classi JavaScript, quali affermazioni sono vere?
Was this page helpful?