Egenskaben "prototype" er meget brugt i JavaScript-kernen. Alle indbyggede konstruktør-funktioner bruger den.
Lad os først se på et par detaljer, og så hvordan man bruger den til at tilføje nye evner til indbyggede objekter.
Object.prototype
Lad os antage at vi vil outputte et tomt objekt:
let obj = {};
alert( obj ); // "[object Object]" ?
Hvor er den kode der genererer strengen "[object Object]"? Det er en indbygget toString-metode, men hvor er den? Det tomme objekt obj har ingen toString-metode.
…Men den korte notation obj = {} er det samme som obj = new Object(), hvor Object er en indbygget objekt konstruktør funktion, med sin egen prototype der refererer til et stort objekt med toString og andre metoder.
Her er hvad der sker:
Når new Object() kaldes (eller et literalt objekt {...} skabes), så sættes[[Prototype]] til væriden af Object.prototype ifølge reglen vi diskuterede i forrige kapitel:
Så når obj.toString() kaldes, bliver metoden taget fra Object.prototype.
Vi kan tjekke det på følgende måde:
let obj = {};
alert(obj.__proto__ === Object.prototype); // true
alert(obj.toString === obj.__proto__.toString); //true
alert(obj.toString === Object.prototype.toString); //true
Bemærk at der findes ikke flere [[Prototype]] i kæden over Object.prototype:
alert(Object.prototype.__proto__); // null
Andre indbyggede prototyper
Andre indbyggede objekter som Array, Date, Function og andre gemmer metoder i deres prototyper.
For eksempel, når vi opretter en array [1, 2, 3], bruges den indbyggede new Array() konstruktør internt. Så bliver Array.prototype dens prototype og leverer metoder. Det er meget effektivt i forhold til hukommelsen.
Fra specifikationen gælder det, at alle indbyggede prototyper har Object.prototype på toppen. Det er hvorfor nogle siger “alt nedarver fra objekter”.
Her er et overordnet billede (af 3 indbyggede for overblikkets skyld):
Lad os se på prototyperne manuelt:
let arr = [1, 2, 3];
// Den nedarver fra Array.prototype
alert( arr.__proto__ === Array.prototype ); // true
// som nedarver fraObject.prototype
alert( arr.__proto__.__proto__ === Object.prototype ); // true
// og med værdien null aller øverst.
alert( arr.__proto__.__proto__.__proto__ ); // null
Nogle gange er der overlap i mulighederne. For eksempel har Array.prototype sin egen toString der oplister dens elementer separeret af kommaer, og Object.prototype har sin oprindelige toString der returnerer "[object Object]". Hvilken en bruges?
let arr = [1, 2, 3]
alert(arr); // 1,2,3 <-- resultatet kommer fra Array.prototype.toString
Object.prototype har toString som den allerførste i kæden, men Array.prototype er tættere på vores kald, så det er den variant der benyttes.
Udviklerværktøjer som Chrome developer konsollen viser også nedarvning. Skriv f.eks.console.dir([1,2,3]) (ikke console.log) for at se et indbygget objekt:
Andre indbyggede objekter fungerer på samme måde. Selv funktioner – de er objekter af den indbyggede Function konstruktør, og deres metoder (call/apply og andre) bliver taget fra Function.prototype. Funktioner har også deres egen toString.
function f() {}
alert(f.__proto__ == Function.prototype); // true
alert(f.__proto__.__proto__ == Object.prototype); // true, nedarver fra Object
Primitiver
En mere indviklet ting sker med strenge, tal og boolske værdier.
Som vi husker, er de ikke objekter. Men hvis vi forsøger at tilgå deres egenskaber, bliver midlertidige wrapper-objekter oprettet ved brug af de indbyggede konstruktører String, Number og Boolean. De leverer metoderne og forsvinder igen.
Disse objekter bliver skabt usynligt for os, og de fleste browsere optimerer dem ud, men specifikationen beskriver det præcist på denne måde. Metoderne for disse objekter befinder sig også i deres prototyper, tilgængelige som String.prototype, Number.prototype og Boolean.prototype.
null og undefined har ingen objekt-wrapperSpecielle værdier som null og undefined er anderledes. De har ingen objekt-wrapper, så metoder og egenskaber er ikke tilgængelige for dem. Og der er ingen tilsvarende prototyper.
Ændring af native prototyper
Native prototyper kan ændres. For eksempel, hvis vi tilføjer en metode til String.prototype, bliver den tilgængelig for alle strenge:
String.prototype.show = function() {
alert(this);
};
"BOOM!".show(); // BOOM!
Mens vi udvikler kan der være ideer om nye indbyggede metoder vi gerne vil have, og vi kan blive fristet til at tilføje dem til native prototyper. Men det er generelt et dårligt idé.