JavaScript mette a disposizione un ottimo strumento per utilizzare l’ereditarietà. Tutti i tipi JavaScript (ad eccezione di null e undefined) hanno la proprietà constructor che consente di identificare il tipo di classe dell’oggetto e che viene ereditata dalle sottoclassi.
Vediamo come accedere a constructor:
console.log(myNum.constructor === Number); // => true
I numeri hanno come constructor Number, i booleani hanno Boolean, le stringhe hanno String e i simboli hanno Symbol (introdotti in ES6).
L’ereditarietà con i tipi primitivi
I tipi primitivi possono essere wrappati (come accade in Java ) in oggetti, in particolare possono essere utilizzati solo con i numeri, le stringhe e i booleani:
new String("Datrevo");
new Boolean(true);
L’utilità dei tipi wrapper è quella di poter inserire proprietà e metodi personalizzati alle classi, quindi di poter utilizzare i tipi primitivi come veri e propri oggetti. Attenzione però che i tipi wrapper hanno lo stesso constructor dei tipi primitivi (per questo motivo per conoscere il tipo di un oggetto è consigliato utilizzare typeof).
console.log(myBoolean.constructor === Boolean); // => true
var myBooleanWrapper = new Boolean(true);
console.log(myBooleanWrapper.constructor === Boolean); // => true
Ereditare dagli altri oggetti sfruttando il prototipo con prototype
In JavaScript un oggetto può ereditare tutte le proprietà di un altro oggetto, l’oggetto da cui si eredita rappresenta il prototipo (prototype) dell’oggetto appena creato. Ogni qualvolta viene creato un nuovo oggetto viene creato un collegamento con il suo prototipo e alle sue proprietà. Tutti gli oggetti ereditano da Object che definisce un insieme di funzioni come hasOwnProperty() utilizzata per verificare se l’oggetto contiene una determinata proprietà.
myField: 1
};
console.log(myObject.hasOwnProperty('myField')); // => true
Per creare un nuovo oggetto basato su un prototipo di un altro oggetto è possibile utilizzare la funzione Object.create(myPrototype). Ad esempio:
myField1: 1
};
var myObject2 = Object.create(myObject1);
console.log(myObject2.myField1); // => 1
Dopo aver definito un oggetto myObject1 che eredita automaticamente da Object.prototype, creiamo un nuovo oggetto (myObject2) il cui prototipo è dato dall’oggetto definito in precedenza (myObject1). A questo punto è possibile accedere ai valori dell’oggetto da cui si è estesi e visualizzare le sue proprietà. la proprietà viene cercata in un primo momento nell’oggetto stesso, se non presente viene risalita la catena di prototipi fino ad arrivare, eventualmente, ad Object.
I vettori ereditano da Array.prototype (che offre metodi come indexOf, forEach, ecc.), anche Array eredita eredita da Object.prototype, così come la funzioni ereditano da Function.prototype (che contiene metodi come call, bind, ecc.). Object è l’unico oggetto che ha prototipo null.
Quando si crea una sottoclasse la proprietà constructor non viene automaticamente settata al nuovo oggetto e bisogna farlo manualmente. Ad esempio:
};
function myFuncion2() {
};
myFuncion2.prototype = Object.create(myFuncion1.prototype);
console.log(myFuncion2.prototype.constructor === myFuncion1); // => true
console.log(myFuncion2.prototype.constructor === myFuncion2); // => false
myFuncion2.prototype.constructor = myFuncion2;
console.log(myFuncion2.prototype.constructor === myFuncion1); // => false
console.log(myFuncion2.prototype.constructor === myFuncion2); // => true
Il mancato aggiornamento del prototype di myFuncion2 è dovuto al myFuncion1.prototype.clone() che richiama this.constructor() per creare un clone di se stesso. In quel momento però, il costruttore punta ancora a myFuncion1.
L’operatore instanceof in JavaScript
L’operatore instanceof consente di determinare se un oggetto ha lo stesso prototipo di un altro oggetto. Quest’operatore però, restituisce true anche quando si tratta di una sottoclasse:
};
function myFunction2() {
};
myFunction2.prototype = Object.create(myFunction1.prototype);
myFunction2.prototype.constructor = myFunction2;
var myObject1 = new myFunction1();
var myObject2 = new myFunction2();
console.log(myObject2 instanceof myFunction1); // => true
console.log(myObject2 instanceof myFunction2); // => true
In questi casi la proprietà constructor ci consente di determinare la vera istanza della classe:
console.log(myObject2.constructor === myFunction2); // => true
Gli oggetti funzione in JavaScript hanno un nome che viene memorizzato nel campo name del constructor. Questo campo ritorna il nome della funziona o una stringa vuota per le funzioni anonime.
};
var myVar = new myFunction1();
console.log(myVar.constructor.name); // => myFunction1