27. februar 2026

F.prototype

Du husker nok at nye objekter kan oprettes med en konstruktør funktion, som f.eks. new F().

Hvis F.prototype er et objekt, så bruger new-operatoren det til at sætte [[Prototype]] for det nye objekt.

Bemærk venligst:

JavaScript har haft nedarvning fra prototype fra begyndelsen. Det er en af sprogets kernetræk.

Men i gamle dage var der ikke nogen direkte adgang til det. Den eneste ting der virkede pålideligt var en "prototype" egenskab i konstruktør funktioner som vi beskriver i dette kapitel. Så der er mange scripts der stadig bruger denne tilgang.

Bemærk at udtrykket F.prototype her betyder en regulær egenskab kaldet "prototype" på objektet F. Det lyder som noget tæt på selve begrebet “prototype”, men her mener vi faktisk bare en regulær egenskab med det navn.

Her er et eksempel:

let animal = {
  eats: true
};

function Rabbit(name) {
  this.name = name;
}

Rabbit.prototype = animal;

let rabbit = new Rabbit("Hvid kanin"); //  rabbit.__proto__ == animal

alert( rabbit.eats ); // true

Når vi skriver Rabbit.prototype = animal betyder det bogstaveligt talt: “Når en ny Rabbit skabes, så sørg for at dens [[Prototype]] sættes til animal”.

Det resulterer i følgende:

På billedet er "prototype" en horisontal pil, hvilket betyder en regulær egenskab, og [[Prototype]] er en lodret pil, betyder nedarvning af rabbit fra animal.

F.prototype bruges kun ved new F

Egenskaben F.prototype bruges kun når new F kaldes, og den sætter [[Prototype]] på det nye objekt.

Hvis F.prototype-egenskaben ændres efter objektet er skabt (F.prototype = <another object>), så vil nye objekter skabt med new F have et andet objekt som [[Prototype]], mens eksisterende objekter beholder det gamle.

Standard F.prototype, konstruktør egenskab

Alle funktioner har egenskaben "prototype" selvom vi ikke angiver den.

Den standard "prototype" er et objekt med den ene egenskab constructor som peger tilbage på funktionen selv.

Som dette:

function Rabbit() {}

/* standard prototype
Rabbit.prototype = { constructor: Rabbit };
*/

Det kan vi tjekke ved at se på constructor egenskaben:

function Rabbit() {}
// som standard:
// Rabbit.prototype = { constructor: Rabbit }

alert( Rabbit.prototype.constructor == Rabbit ); // true

Hvis vi ikke gør noget andet vil egenskaben constructor være tilgængelig for alle kaniner gennem [[Prototype]]:

function Rabbit() {}
// som standard:
// Rabbit.prototype = { constructor: Rabbit }

let rabbit = new Rabbit(); // nedarver fra {constructor: Rabbit}

alert(rabbit.constructor == Rabbit); // true (fra prototype)

Vi kan bruge constructor egenskaben til at skabe et nyt objekt med samme konstruktør som det eksisterende objekt.

Som her:

function Rabbit(name) {
  this.name = name;
  alert(name);
}

let rabbit = new Rabbit("Hvid kanin");

let rabbit2 = new rabbit.constructor("Sort kanin");

Det er praktisk når vi har et objekt og ikke ved hvilken konstruktør det blev skabt med (f.eks. hvis det kommer fra en 3rd party bibliotek), og vi skal skabe et andet objekt af samme type.

Men den vigtigste ting at huske på om "constructor" er at…

…JavaScript sikrer ikke selv at værdien i "constructor" er korrekt.

Ja, den eksisterer i standard "prototype" for funktioner, men det er det. Hvad der sker senere – det er helt op til os.

Det gælder særligt, hvis vi erstatter standard prototypen i sin helhed, så vil der ikke længere være nogen "constructor" i den.

For eksempel:

function Rabbit() {}
Rabbit.prototype = {
  jumps: true
};

let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false

Så for at beholde den korrekte "constructor" kan vi vælge at tilføje/fjerne egenskaber til standard "prototype" i stedet for at overskrive hele prototypen:

function Rabbit() {}

// Overskriver ikke Rabbit.prototype helt
// vi tilføjer bare noget til den
Rabbit.prototype.jumps = true
// standard Rabbit.prototype.constructor er bevaret

Alternativt kan du genskabe egenskaben constructor manuelt, hvis du erstatter hele prototypen:

Rabbit.prototype = {
  jumps: true,
  constructor: Rabbit
};

// nu er constructor rigtig igen, fordi vi tilføjede den tilbage til objektet

Opsummering

I dette kapitel beskrev vi kort måden at sætte en [[Prototype]] for objekter skabt via en konstruktør funktion. Senere vil vi se mere avancerede programmeringsmønstre, der bygger på det.

Alt er egentlig ganske enkelt. Her er nogle få bemærkninger for at gøre tingene helt klart:

  • Egenskaben F.prototype (ikke at forveksle med [[Prototype]]) sætter [[Prototype]] på nye objekter når new F() kaldes.
  • Værdien af F.prototype bør enten være et objekt eller null: andre værdier virker ikke.
  • Egenskaben "prototype" har kun denne specielle effekt når den er sat på en konstruktør funktion, og kaldes med new.

På regulære objekter er prototype ikke noget specielt:

let user = {
  name: "John",
  prototype: "Bla-bla" // magien er væk!
};

Som standard har alle funktioner F.prototype = { constructor: F }, så vi kan få konstruktøren af et objekt ved at tilgå dets "constructor" egenskab.

Opgaver

vigtighed: 5

I koden nedenfor opretter vi new Rabbit, og forsøger derefter at ændre dens prototype.

I starten har vi følgende kode:

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};

let rabbit = new Rabbit();

alert( rabbit.eats ); // true
  1. Vi har tilføjet endnu en streng (fremhævet). Hvad viser alert nu?

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    Rabbit.prototype = {};
    
    alert( rabbit.eats ); // ?
  2. …Og hvis koden ser således ud (har erstattet en linje)?

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    Rabbit.prototype.eats = false;
    
    alert( rabbit.eats ); // ?
  3. Og, hvad med denne her (har erstattet en linje)?

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    delete rabbit.eats;
    
    alert( rabbit.eats ); // ?
  4. Sidste variant?

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    delete Rabbit.prototype.eats;
    
    alert( rabbit.eats ); // ?

Svarene er:

  1. true.

    Tildelingen til Rabbit.prototype sætter [[Prototype]] for nye objekter, men påvirker ikke eksisterende objekter. Så rabbit har stadig den gamle prototype, hvor eats er true.

  2. false.

    Objekter bliver tildelt med reference til det oprindelige objekt. Objektet fra Rabbit.prototype er ikke kopieret, det er stadig et enkelt objekt refereret både af Rabbit.prototype og af [[Prototype]]rabbit.

    Så når vi ændrer dets indhold via én reference, er det synligt via den anden reference.

  3. true.

    Alle delete operationer udføres direkte på objektet. Her forsøger delete rabbit.eats at fjerne eats egenskaben fra rabbit, men det har ikke den egenskab. Så operationen har ingen effekt.

  4. undefined.

    Egenskaben eats er slettet fra prototypen, den eksisterer ikke længere.

vigtighed: 5

Forestil dig, vi har et vilkårligt objekt obj, skabt af en konstruktør funktion – vi ved ikke hvilken, men vi vil gerne skabe et nyt objekt med samme konstruktør.

Kan vi gøre det sådan?

let obj2 = new obj.constructor();

Giv et eksempel på en konstruktør funktion for obj som lader sådan kode virke korrekt. Og et eksempel som gør det forkert.

Vi kan bruge denne tilgang hvis vi er sikre på at "constructor" egenskaben har den korrekte værdi.

For eksempel, hvis vi ikke rører ved standard "prototype", så virker denne kode altid:

function User(name) {
  this.name = name;
}

let user = new User('Jan');
let user2 = new user.constructor('Helene');

alert( user2.name ); // Helene (virker!)

Det virker fordi User.prototype.constructor == User.

…Men hvis en overskriver User.prototype uden at genskabe constructor som reference til User, så vil det fejle.

For eksempel:

function User(name) {
  this.name = name;
}
User.prototype = {}; // (*)

let user = new User('Jan');
let user2 = new user.constructor('Helene');

alert( user2.name ); // undefined

Hvorfor er user2.name undefined?

Sådan her virker new user.constructor('Helene'):

  1. Først kigger den efter constructor i user. Intet fundet.
  2. Derefter følger den prototypen. Prototypen af user er User.prototype, og den har også ingen constructor (fordi vi “glemte” at genskabe den!).
  3. Går videre op i kæden, User.prototype er et almindeligt objekt, og dens prototype er den indbyggede Object.prototype.
  4. Til sidst, for den indbyggede Object.prototype, findes der en indbygget Object.prototype.constructor == Object. Så bruges denne.

Til sidst har vi altså noget der oversat ser ud som let user2 = new Object('Helene').

Helt sikkert ikke det vi vil. Vi ønsker at skabe new User, ikke new Object. Det er resultatet af den manglende constructor.

(Bare, i det tilfælde, at du er nysgerrig: Kaldet til new Object(...) konverterer dets argument til et objekt. Det er en teoretisk ting, i praksis kalder ingen new Object med en værdi, og generelt bruger vi ikke new Object til at lave objekter).

Tutorial-oversigt

Kommentarer

læs dette før du kommenterer…
  • Hvis du har forslag til forbedringer - så opret venligst et GitHub-issue eller en pull request i stedet for at kommentere.
  • Hvis du ikke forstår noget i artiklen - så uddyb venligst.
  • For at indsætte få ord kode, brug <code>-taggen, for flere linjer - omslut dem i <pre>-tag, for mere end 10 linjer - brug en sandbox (plnkr, jsbin, codepen…)