Class extends Object?
Come ormai sappiamo, tutti gli oggetti, normalmente ereditano da Object.prototype
ed hanno accesso ai metodi generici di Object
, come hasOwnProperty
etc.
Ad esempio:
class Rabbit {
constructor(name) {
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
// hasOwnProperty viene ereditato da Object.prototype
alert( rabbit.hasOwnProperty('name') ); // true
Ma se lo invocassimo esplicitamente in questo modo: "class Rabbit extends Object"
, allora il risultato sarebbe diverso da un semplice "class Rabbit"
?
Qual’è la differenza?
Qui vediamo un esempio (perché non funziona? è possibile sistemarlo?):
class Rabbit extends Object {
constructor(name) {
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
alert( rabbit.hasOwnProperty('name') ); // Error
Come prima cosa, cerchiamo di capire perché il codice non funziona.
La motivazione appare piuttosto ovvia se proviamo ad eseguire il codice. Un classe che eredita, deve invocare super()
. Diversamente, il valore di "this"
non sarà “definito”.
Vediamo come sistemarlo:
class Rabbit extends Object {
constructor(name) {
super(); // dobbiamo chiamare il costruttore padre della classe da cui stiamo ereditando
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
alert( rabbit.hasOwnProperty('name') ); // true
Ma non è tutto.
Anche dopo questo fix, c’è ancora un grande differenza tra "class Rabbit extends Object"
e class Rabbit
.
Come già sappiamo, la sintassi “extends” imposta due prototype:
- Tra
"prototype"
del costruttore (per i metodi). - Tra i costruttori stessi (per i metodi statici).
Nel nostro caso, class Rabbit extends Object
significa:
class Rabbit extends Object {}
alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) true
In questo modo, tramite Rabbit
abbiamo accesso ai metodi statici di Object
, come nell’esempio:
class Rabbit extends Object {}
// normalmente invochiamo Object.getOwnPropertyNames
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b
Ma se non estendiamo l’oggetto, conextends Object
, allora Rabbit.__proto__
non sarà impostato a Object
.
Qui una demo:
class Rabbit {}
alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) false (!)
alert( Rabbit.__proto__ === Function.prototype ); // come qualsiasi funzione di default
// errore, funzione non esistente in Rabbit
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error
Quindi Rabbit
, in questo caso, non fornisce l’accesso ai metodi statici di Object
.
In ogni caso, Function.prototype
possiede metodi “generici”, come call
, bind
etc. Questi saranno disponibili in entrambi i casi, grazie al costruttore di Object
, Object.__proto__ === Function.prototype
.
Come mostrato in figura:
Quindi, per riassumere, ci sono due principali differenze:
class Rabbit | class Rabbit extends Object |
---|---|
– | dobbiamo invocare super() nel costruttore |
Rabbit.__proto__ === Function.prototype |
Rabbit.__proto__ === Object |