Prototypes Flashcards
Wat is een prototype en waarvoor is het goed? Geef daarnaast ook een voorbeeld van hoe we een prototype kunnen definiëren voor twee object literals.
Een prototype is een object waarvan de leden kunnen overgeërfd worden door meerdere andere objecten. Dit is goed zodat er geen gemeenschappelijke code dubbel in het geheugen staat.
Bijvoorbeeld: stel we hebben de objecten a en b die beiden dezelfde methode bevatten:
const a = {
greet(){
return ‘hello’;
}
const b = {
greet(){
return ‘hello’;
}
Omdat ze dezelfde methode delen is het beter om deze methode in een appart object te plaatsen (het prototype) en de objecten a en b deze methode van het prototype latan overerven:
const proto = {
greet(){
return ‘hello’;
}
const a = {};
const b = {};
Object.setPrototypeOf(a, proto);
Object.setPrototypeOf(b, proto);
Hoe wordt een prototype gedefinieerd voor instanties van factories?
Dit kan door eerst het prototype object aan te maken:
const proto = {
greet(){
return ‘hello’;
}
Eens deze is aangemaakt kan dit prototype aan de hand van Object.setPrototypeOf gelinkt worden aan de instanties van de factory function. Het leggen van deze link gebeurt in de facotry function zelf:
function someFactory() {
const obj = {};
Object.setPrototypeOf(obj, proto);
// voeg andere eigenschappen en methoden toe
return obj ; }
Of:
function someFactory() {
const obj = Object.setPrototypeOf( {
// eigenschappen en methoden
}, proto);
return obj ;
}
Wat doet Object.setPrototypeOf achter de schermen?
Het gaat de [[ Prototype ]] eigenschap van het object dat wordt meegegeven als het eerste argument van de methode, initialiseren met het object dat wordt meegegeven als het tweede argument van de methode. Het voorziet het object dus als het ware van een prototype.
Opgelet: Het veranderen van de [[ Prototype ]] eigenschap van een object is op het moment van schrijven een weinig performante operatie (zie mdn docs). Zolang De javascript engine developers de performantie van dergelijke operatie niet hebben verbeterd is het aangeraden om Object.create te gebruiken.
- Wat is de prototype chain?
- Hoe wordt er gezocht naar een eigenschap of methode in deze prototype chain?
- In javascript is het zo dat elk object, op één na (de root prototype), een prototype heeft. Omdat prototypes zelf ook objecten zijn wilt dit zeggen dat ze zelf ook een prototype hebben. Het prototype van een object kan je vinden onder de [[ Prototype ]] eigenschap van dat object.
Als elk prototype zelf een prototype heeft, dan krijgen we een lijst van aaneengelinkte prototypes die eindigt bij het root prototype. Deze structuur wordt de prototype chain wordt genoemd.
- Wanneer een object een methode oproept, dan zal de javascript engine de methode eerst gaan opzoeken in het object zelf. Als de methode daar niet wordt gevonden dan zal de methode in het prototype van dat object gezocht worden. Als de methode daar niet wordt gevonden, dan zal de methode op zijn beurt in het prototype van dat prototype gezocht worden. Dit gaat door totdat het laatste prototype in de keten (root prototype) bereikt wordt. Als de methode daar niet gevonden wordt dan zal er in de console een foutmelding verschijnen met de boodschap dat de methode niet bestaat
Wat is prototypische overerving
Het mechanisme dat ervoor zorgt dat een object gebruik kan maken van data dat in een ander object is gedefinieerd.
Wat is het verschil tussen de [[ Prototype ]] eigenschap van objecten en de prototype eigenschap van functies?
De [[ Prototype ]] eigenschap van een object a bevat het prototype object van dat object a. Omdat functies achter de schermen zelf ook objecten zijn, wilt dit zeggen dat ook functies de [[ Prototype ]] eigenschap bezitten.
Er is echter 1 eigenschap dat alleen functies hebben en dat is de prototype eigenschap (niet te verwarren met [[ Prototype ]] ). De prototype eigenschap zal het object bezitten dat zal dienen als het prototype voor de instanties die voortvloeien uit de functie (indien de functie gebruikt wordt als constructor).
Dit wilt zeggen dat wanneer eeb object b wordt aangemaakt door middel van een constructor functie. Dat de [[prototype]] eigenschap van dit object b zal verwijzen naar de prototype eigenschap van de constructor functie. Dit gebeurt automatisch door de new operator.
Hoe wordt een prototype gedefinieerd voor instanties van constructors?
Constructors zijn functies, en functies bezitten de prototype eigenschap (niet te verwarren met [[ Prototype ]]). Deze eigenschap verwijst naar een object dat als als prototype zal dienen voor de instanties van de constructor. Dit wilt zeggen dat het prototype object al bestaat en dat het daarom niet meer moet worden aangemaakt.
Wat wel moet gebeuren is dit prototype populeren met de leden die we de instanties van de constructor willen laten overerven. Dit kan als volgt:
NaamConstructor.prototype.NaamToeTeVOegenMethode = function() { … };
Methoden kunnen ook toegevoegd of verwijderd worden nadat instanties zijn aangemaakt.
Naar wat verwijst de prototype eigenschap van een functie initieel?
Initieel verwijst de prototype eigenschap van een functie naar een object die zelf maar 1 enkele eigenschap bezit, namelijk: de constructor eigenschap. De constructor eigenschap van dit object naar de huidige functie zelf.
In de constructor functie is het mogelijk om gebruik te maken van de eigenschappen en methoden die in het prototype zijn gedefinieerd, bijvoorbeeld:
// constructor
function Circle(radius) {
this.radius = radius;
this.move = function () {
// we maken gebruik van de draw methode
this.draw();
console.log(“move”);
};
}
// prototype
Circle.prototype.draw = function () {
console.log(draw
);
};
Is het omgekeerde ook mogelijk? zo ja, verklaar hoe dit mogelijk is.
Ja dit is mogelijk.
Om dit te verklaren geven we een voorbeeld waarbij een functie in het prototype gebruik maakt van een functie in de constructor:
function Circle(radius) {
this.radius = radius;
this.move = function () {
console.log(‘move’);
};
}
// prototype
Circle.prototype.draw = function () {
this.move();
console.log(draw
);
};
const c = new Circle(5);
c.draw();
Het resultaat van c.draw zal resulteren in:
move
draw
De reden dat dit mogelijk is, is doordat bij de oproep van de draw methode, de this waarde van deze draw methode zal geïnitialiseerd worden met het object dat de draw methode heeft opgeroepen. Dit wilt zeggen dat als de regel this.move() wordt uitgevoerd, dat de move functie eerst gezocht zal worden in het object zelf. Omdat deze functie zich daar degelijk bevindt, zal het ook worden uitgevoerd.
Bekijke volgdende Constructors:
function Circle(radius, color) {
this.radius = radius;
this.color = color;
}
Circle.prototype.duplicate = function () {
console.log(‘duplicate’);
};
function Square(size, color) {
this.size = size;
this.color = color;
}
Square.prototype.duplicate = function () {
console.log(‘duplicate’);
};
const c = new Circle(5, ‘red’);
const s = new Square(10, ‘blue’);
- Deze code werkt maar is niet optimaal. Leg uit waarom?
- Leg uit hoe we dit kunnen verbeteren en implemnteer de oplossing.
- Beide constructoren hebben de duplicate methode en de color eigenschap gemeenschappelijk. Dit is redundante code dat voor twee problemen zorgt:
- Als de methode duplicate of de eigenschap color moet worden aangepast dan moet dit op twee plaatsen gebeuren.
- Dit neemt meer geheugen omdat zowel de methode als de eigenschap twee keer worden opgeslagen. - Een betere manier is om de gemeenschappelijke leden te plaatsen in een superconstructor (bijv Shape) en de constructors Square en Circle de eigenschappen en methoden van deze superconstructor te laten overerven. Dit kan als volgt:
function Shape(color) {
this.color = color;
}
Shape.prototype.duplicate = function () {
console.log(‘duplicate’);
};
function Circle(radius, color) {
this.radius = radius;
Shape.call(this, color);
}
function Square(size, color) {
this.size = size;
Shape.call(this, color);
}
function extend(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
}
extend(Circle, Shape);
extend(Square, Shape);
const c = new Circle(5, ‘red’);
const s = new Square(10, ‘blue’);
Stel je hebt een Circle constructor dat de methoden en eigenschappen overeft zijn parent constructor Shape. Shape heeft de volgende methode in zijn prototype eigenschap:
Shape.prototype.duplicate = function () {
console.log(‘duplicate shape’);
};
Deze methode wordt overschreven in Circle (method overriding):
Circle.prototype.duplicate = function () {
console.log(‘duplicate circle’);
};
Stel nu dat je in de body van de duplicate methode van Cirlce, de duplicate methode van Shape wilt oproepen, hoe doe je dit dan?
Circle.prototype.duplicate = function () {
Shape.prototype.duplicate();
console.log(‘duplicate’);
};
Stel dat Circle de child constructor is van Shape. Waar moet je dan op letten wanneer je methoden toevoegd aan de prototype eigenschap van Circle?
Dat de methoden definities steeds na de extend oproep komen, dus:
extend(Circle, Shape);
Circle.prototype.duplicate = function () {
console.log(‘duplicate’);
};
Als je dit niet doet dan zal de protype eigenschap van Circle eerst wel de duplicate methode bevatten maar van zodra dan de extend functie wordt opgeroepen zal de prototype eigenschap vervangen worden door de prototype eigenschap van de parent waardoor de duplicate methode verloren gaat.
Wat is method overriding bij prototypische overving en wat is het voordeel hiervan?
Wanneer we een methode hebben in een prototype, die we herimplementeren in een child prototype dan spreken we over method overriding.
Het voordeel van method overriding is dat een prototype zijn eigen implementatie kan voorzien voor een methode die hij overerfd van een parent prototype.