Bruk av 'prototype' vs. 'this' i JavaScript?


Hva er forskjellen mellom

var A = function () {
    this.x = function () {
        //do something
    };
};

og

var A = function () { };
A.prototype.x = function () {
    //do something
};

Svar #1

Eksemplene har veldig forskjellige utfall.

Før man ser på forskjellene, bør følgende bemerkes:

  • En konstruktørs prototype gir en måte å dele metoder og verdier mellom forekomster via forekomstens private [[Prototype]]-egenskap.
  • En funksjons this settes av hvordan funksjonen blir kalt eller ved bruk av bind (ikke diskutert her). Når en funksjon kalles på et objekt (f.eks. myObj.method()), refererer this innen metoden til objektet. Når this ikke settes av kall eller ved bruk av bind, blir det standard til det globale objektet (window i en nettleser) eller i strict mode, forblir det undefined.
  • JavaScript er et objektorientert språk, altså er de fleste verdier objekter, inkludert funksjoner. (Strenger, tall og booleaner er ikke objekter.)

Så her er utsnittene det gjelder:

var A = function () {
    this.x = function () {
        //do something
    };
};

I dette tilfellet blir variabelen A tildelt en verdi som er en referanse til en funksjon. Når den funksjonen blir kalt ved å bruke A(), blir ikke funksjonens this satt av anropet, så det settes som standard til det globale objektet, og uttrykket this.x tilsvarer window.x. Resultatet er at en referanse til funksjonsuttrykket på høyre side blir tildelt window.x.

I tilfelle:

var A = function () { };
A.prototype.x = function () {
    //do something
};

noe veldig annerledes skjer. I den første linjen blir variabelen A tildelt en referanse til en funksjon. I JavaScript har alle funksjonsobjekter en prototype-egenskap som standard, så det er ingen separat kode for å opprette et A.prototype-objekt.

I den andre linjen blir A.prototype.x tildelt en referanse til en funksjon. Dette vil opprette en x-egenskap hvis den ikke finnes, eller tildele en ny verdi hvis den gjør det. Så forskjellen med det første eksemplet er at objektets x-egenskap er involvert i uttrykket.

Et annet eksempel er nedenfor. Det ligner på det første (og kanskje det du mente å spørre om):

var A = new function () {
    this.x = function () {
        //do something
    };
};

I dette eksempelet er new-operatoren lagt til før funksjonsuttrykket slik at funksjonen blir kalt som en konstruktør. Når den kalles med new, settes funksjonens this til å referere til et nytt objekt hvis private [[Prototype]]-egenskap er satt til å referere til konstruktørens offentlige prototype. Så i tildelingssetningen vil x-egenskapen bli opprettet på dette nye objektet. Når den kalles som en konstruktør, returnerer en funksjon sitt this-objekt som standard, så det er ikke behov for en egen return this;-setning.

For å sjekke at A har en x-egenskap:

console.log(A.x) // function () {
                 //   //do something
                 // };

Dette er en uvanlig bruk av new siden den eneste måten å referere til konstruktøren på er via A.constructor. Det ville være mye vanligere å gjøre:

var A = function () {
    this.x = function () {
        //do something
    };
};
var a = new A();

En annen måte å oppnå et lignende resultat på er å bruke et umiddelbart kalt funksjonsuttrykk:

var A = (function () {
    this.x = function () {
        //do something
    };
}());

I dette tilfellet tilordnet A returverdien av å kalle funksjonen på høyre side. Her igjen, siden this ikke er satt i kallet, vil det referere til det globale objektet, og this.x er effektivt window.x. Siden funksjonen ikke returnerer noe, vil A ha en verdi av undefined.

Disse forskjellene mellom de to tilnærmingene kommer også til uttrykk hvis du serialiserer og de-serialiserer Javascript-objektene dine til/fra JSON. Metoder definert på et objekts prototype blir ikke serialisert når du serialiserer objektet, noe som kan være praktisk når du for eksempel bare vil serialisere datadelen av et objekt, men ikke dets metoder:

var A = function () { 
    this.objectsOwnProperties = "are serialized";
};
A.prototype.prototypeProperties = "are NOT serialized";
var instance = new A();
console.log(instance.prototypeProperties); // "are NOT serialized"
console.log(JSON.stringify(instance)); 
// {"objectsOwnProperties":"are serialized"} 

Relaterte spørsmål:

  • Hva betyr det at JavaScript er et prototypbasert språk?
  • Hva er omfanget til en funksjon i JavaScript?
  • Hvordan fungerer nøkkelordet "this"?

Sidestrek: Det kan hende det ikke er noen betydelige minnebesparelser mellom de to tilnærmingene, men å bruke prototypen for å dele metoder og egenskaper vil sannsynligvis bruke mindre minne enn at hver instans har sin egen kopi.

JavaScript er ikke et lavnivåspråk. Det kan være lite verdifullt å tenke på prototyping eller andre arvemønstre som en måte å eksplisitt endre måten minne tildeles på.

Svar #2

Som andre har sagt, resulterer den første versjonen, som bruker "this", i at hver instans av klasse A har sin egen uavhengige kopi av funksjonsmetoden "x". Mens bruk av "prototype" vil bety at hver instans av klasse A vil bruke den samme kopien av metoden "x".

Her er litt kode for å vise denne subtile forskjellen:

// x is a method assigned to the object using "this"
var A = function () {
    this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
    this.x = function() { alert( value ); }
};

var a1 = new A();
var a2 = new A();
a1.x();  // Displays 'A'
a2.x();  // Also displays 'A'
a1.updateX('Z');
a1.x();  // Displays 'Z'
a2.x();  // Still displays 'A'

// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };

B.prototype.updateX = function( value ) {
    B.prototype.x = function() { alert( value ); }
}

var b1 = new B();
var b2 = new B();
b1.x();  // Displays 'B'
b2.x();  // Also displays 'B'
b1.updateX('Y');
b1.x();  // Displays 'Y'
b2.x();  // Also displays 'Y' because by using prototype we have changed it for all instances

Som andre har nevnt, finnes det ulike grunner til å velge den ene metoden fremfor den andre. Mitt eksempel er bare ment å tydelig demonstrere forskjellen.

Svar #3

Ta disse 2 eksemplene:

var A = function() { this.hey = function() { alert('from A') } };

mot

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

De fleste her (spesielt de svarene med høyest rangering) prøvde å forklare hvordan de er forskjellige uten å forklare HVORFOR. Jeg tror dette er feil, og hvis du først forstår grunnprinsippene, vil forskjellen bli åpenbar. La oss prøve å forklare grunnprinsippene først...

a) En funksjon er et objekt i JavaScript. HVER objekt i JavaScript får en intern egenskap (som betyr at du ikke kan få tilgang til den som andre egenskaper, bortsett fra kanskje i nettlesere som Chrome), ofte referert til som __proto__ (du kan faktisk skrive anyObject.__proto__ i Chrome for å se hva den refererer til. Dette er bare det, en egenskap, ikke mer. En egenskap i JavaScript=en variabel inne i et objekt, ikke mer. Hva gjør variabler? De peker på ting.

Så hva peker denne __proto__-egenskapen på? Vel, vanligvis på et annet objekt (vi forklarer hvorfor senere). Den eneste måten å tvinge JavaScript til at __proto__-egenskapen IKKE peker på et annet objekt, er å bruke var newObj=Object.create(null). Selv om du gjør dette, eksisterer __proto__-egenskapen FORTSATT som en egenskap på objektet, den peker bare ikke på et annet objekt, den peker på null.

Her er hvor de fleste blir forvirret:

Når du oppretter en ny funksjon i JavaScript (som også er et objekt, husk det?), øyeblikkelig etter at den er definert, oppretter JavaScript automatisk en ny egenskap på den funksjonen kalt prototype. Prøv det:

var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined

A.prototype er HELT FORSKJELLIG fra __proto__-egenskapen. I vårt eksempel har 'A' nå TO egenskaper kalt 'prototype' og __proto__. Dette skaper stor forvirring for folk. prototype- og __proto__-egenskapene er på ingen måte relatert, de er separate ting som peker på separate verdier.

Du kan lure på: Hvorfor har JavaScript __proto__-egenskapen opprettet på hvert eneste objekt? Vel, ett ord: delegering. Når du kaller en egenskap på et objekt og objektet ikke har den, så ser JavaScript etter objektet som refereres av __proto__ for å se om det kanskje har det. Hvis det ikke har det, så ser det på det objektets __proto__-egenskap, og så videre... til kjeden slutter. Dermed navnet prototypekjed. Selvfølgelig, hvis __proto__ ikke peker på et objekt og i stedet peker på null, vel, tøff flaks, JavaScript innser det og vil returnere undefined for egenskapen.

Du lurer kanskje også på, hvorfor JavaScript oppretter en egenskap kalt prototype for en funksjon når du definerer funksjonen? Fordi det prøver å lure deg, ja lure deg til å tro at det fungerer som klassebaserte språk.

La oss gå videre med vårt eksempel og lage et "objekt" av A:

var a1 = new A();

Det skjer noe i bakgrunnen når dette skjedde. a1 er en vanlig variabel som ble tildelt et nytt, tomt objekt.

Faktumet at du brukte operatoren new før et funksjonskall A() gjorde noe EKSTRA i bakgrunnen. Nøkkelordet new opprettet et nytt objekt som nå refererer til a1 og det objektet er tomt. Her er hva som skjer i tillegg:

Vi sa at på hver funksjonsdefinisjon blir det opprettet en ny egenskap kalt prototype (som du kan få tilgang til, i motsetning til __proto__-egenskapen) opprettet? Vel, den egenskapen blir brukt nå.

Så vi er nå på punktet hvor vi har et nybakt tomt a1-objekt. Vi sa at alle objekter i JavaScript har en intern __proto__-egenskap som peker til noe (a1 har det også), enten det er null eller et annet objekt. Det new-operatoren gjør er at den setter den __proto__-egenskapen til å peke på funksjonens prototype-egenskap. Les det igjen. Det er i utgangspunktet dette:

a1.__proto__ = A.prototype;

Vi sa at A.prototype ikke er annet enn et tomt objekt (med mindre vi endrer det til noe annet før vi definerer a1). Så nå peker i utgangspunktet a1.__proto__ på det samme som A.prototype peker på, nemlig det tomme objektet. De peker begge på det samme objektet som ble opprettet da denne linjen ble kjørt:

A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}

Nå skjer det en annen ting når var a1=new A() setningen behandles. I utgangspunktet blir A() kjørt, og hvis A er noe som dette:

var A = function() { this.hey = function() { alert('from A') } };

Alt det der inne i function() { } kommer til å kjøre. Når du når linjen this.hey.., er this endret til a1 og du får dette:

a1.hey = function() { alert('from A') }

Jeg vil ikke forklare hvorfor this endres til a1, men dette er et flott svar for å lære mer.

Så for å oppsummere, når du gjør var a1=new A() skjer det 3 ting i bakgrunnen:

  1. Et helt nytt tomt objekt blir opprettet og tildelt a1. a1={}
  2. Egenskapen a1.__proto__ blir tildelt for å peke på det samme som A.prototype peker på (et annet tomt objekt {} )

  3. Funksjonen A() blir utført med this satt til det nye, tomme objektet som ble opprettet i trinn 1 (les svaret jeg refererte til ovenfor for å forstå hvorfor this endres til a1)

La oss nå prøve å lage et annet objekt:

var a2 = new A();

Trinn 1,2,3 vil gjentas. Legger du merke til noe? Nøkkelordet er gjenta. Trinn 1: a2 vil være et nytt tomt objekt, trinn 2: dens __proto__-egenskap vil peke på det samme som A.prototype peker på, og viktigst av alt, trinn 3: funksjonen A() blir IGJEN utført, noe som betyr at a2 vil få hey-egenskapen som inneholder en funksjon. a1 og a2 har to SEPARATE egenskaper med navnet hey som peker på 2 SEPARATE funksjoner! Vi har nå dupliserte funksjoner i to forskjellige objekter som gjør det samme, oops... Du kan forestille deg minneimplikasjonene av dette dersom vi har 1000 objekter opprettet med new A, ettersom alle funksjonsdeklarasjoner tar mer minne enn noe som tallet 2. Så hvordan kan vi forhindre dette?

Husker du hvorfor __proto__-egenskapen finnes på hvert objekt? For at hvis du henter yoMan-egenskapen på a1 (som ikke eksisterer), vil dens __proto__-egenskap bli konsultert, som, hvis det er et objekt (og i de fleste tilfeller er det det), vil sjekke om det inneholder yoMan, og hvis det ikke gjør det, vil det konsultere det objektets __proto__ osv. Hvis det gjør det, vil det ta den egenskapsverdien og vise den til deg.

Så noen bestemte seg for å bruke dette faktum + det faktum at når du oppretter a1, peker dens __proto__-egenskap til det samme (tomme) objektet som A.prototype peker til, og gjøre dette:

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

Kult! Nå, når du oppretter a1, går det igjen gjennom alle de 3 trinnene ovenfor, og i trinn 3 gjør det ingenting, siden function A() ikke har noe å utføre. Og hvis vi gjør:

a1.hey

Det vil se at a1 ikke inneholder hey, og det vil sjekke dens __proto__-egenskapsobjekt for å se om det har det, noe som er tilfelle.

Med denne tilnærmingen eliminerer vi delen fra trinn 3 hvor funksjoner blir duplisert ved hver ny objektoppretting. I stedet for at a1 og a2 har en separat hey-egenskap, har nå INGEN av dem den. Det har du vel forstått selv nå, antar jeg. Det er det som er fint... hvis du forstår __proto__ og Function.prototype, vil spørsmål som dette være ganske åpenbare.

MERK: Noen mennesker har en tendens til å ikke kalle den interne Prototype-egenskapen for __proto__, jeg har brukt dette navnet gjennom hele innlegget for tydelig å skille det fra Functional.prototype-egenskapen som to forskjellige ting.

Svar #4

I de fleste tilfeller er de i praksis det samme, men den andre versjonen sparer minne fordi det bare finnes én forekomst av funksjonen i stedet for en egen funksjon for hvert objekt.

En grunn til å bruke den første formen er for å få tilgang til "private medlemmer". For eksempel:

var A = function () {
    var private_var = ...;

    this.x = function () {
        return private_var;
    };

    this.setX = function (new_x) {
        private_var = new_x;
    };
};

På grunn av javascripts skaleringsregler er private_var tilgjengelig for funksjonen som er tildelt denne.x, men ikke utenfor objektet.

Svar #5

Det første eksemplet endrer grensesnittet for det objektet alene. Det andre eksemplet endrer grensesnittet for alle objekter av den klassen.

Svar #6

Hvert objekt er knyttet til et prototypeobjekt. Når man prøver å få tilgang til en egenskap som ikke eksisterer, vil JavaScript lete i objektets prototypeobjekt etter den egenskapen og returnere den hvis den finnes.

Egenskapen prototype til en funksjonskonstruktør refererer til prototype-objektet for alle forekomster som opprettes med den funksjonen når man bruker new.

I ditt første eksempel legger du til en egenskap x til hver instans som opprettes med A-funksjonen.

var A = function () {
    this.x = function () {
        //do something
    };
};

var a = new A();    // constructor function gets executed
                    // newly created object gets an 'x' property
                    // which is a function
a.x();              // and can be called like this

I det andre eksempelet legger du til en egenskap på prototype-objektet som alle forekomstene opprettet med A peker på.

var A = function () { };
A.prototype.x = function () {
    //do something
};

var a = new A();    // constructor function gets executed
                    // which does nothing in this example

a.x();              // you are trying to access the 'x' property of an instance of 'A'
                    // which does not exist
                    // so JavaScript looks for that property in the prototype object
                    // that was defined using the 'prototype' property of the constructor

Avslutningsvis, i det første eksemplet tilordnes en kopi av funksjonen til hver instans. I det andre eksemplet deles en enkelt kopi av funksjonen mellom alle instanser.

Svar #7

Det ultimate problemet med å bruke this i stedet for prototype er at når man overstyrer en metode, vil konstruktøren til basklassen fortsatt referere til den overstyrte metoden. Vurder dette:

BaseClass = function() {
    var text = null;

    this.setText = function(value) {
        text = value + " BaseClass!";
    };

    this.getText = function() {
        return text;
    };

    this.setText("Hello"); // This always calls BaseClass.setText()
};

SubClass = function() {
    // setText is not overridden yet,
    // so the constructor calls the superclass' method
    BaseClass.call(this);

    // Keeping a reference to the superclass' method
    var super_setText = this.setText;
    // Overriding
    this.setText = function(value) {
        super_setText.call(this, "SubClass says: " + value);
    };
};
SubClass.prototype = new BaseClass();

var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!

subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

mot :

BaseClass = function() {
    this.setText("Hello"); // This calls the overridden method
};

BaseClass.prototype.setText = function(value) {
    this.text = value + " BaseClass!";
};

BaseClass.prototype.getText = function() {
    return this.text;
};

SubClass = function() {
    // setText is already overridden, so this works as expected
    BaseClass.call(this);
};
SubClass.prototype = new BaseClass();

SubClass.prototype.setText = function(value) {
    BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};

var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

Hvis du synes dette ikke er et problem, så avhenger det av om du kan leve uten private variabler, og om du er erfaren nok til å vite når du ser en lekkasje. I tillegg er det upraktisk å måtte legge konstruktørlogikken etter metode-definisjonene.

var A = function (param1) {
    var privateVar = null; // Private variable

    // Calling this.setPrivateVar(param1) here would be an error

    this.setPrivateVar = function (value) {
        privateVar = value;
        console.log("setPrivateVar value set to: " + value);

        // param1 is still here, possible memory leak
        console.log("setPrivateVar has param1: " + param1);
    };

    // The constructor logic starts here possibly after
    // many lines of code that define methods

    this.setPrivateVar(param1); // This is valid
};

var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0

a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0

mot :

var A = function (param1) {
    this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
    this.publicVar = value; // No private variable
};

var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1

Svar #8

Hva er forskjellen? => Mye.

Jeg tror at this-versjonen brukes for å muliggjøre innkapsling, dvs. dataskjuling. Det hjelper med å manipulere private variabler.

La oss se på følgende eksempel:

var AdultPerson = function() {

  var age;

  this.setAge = function(val) {
    // some housekeeping
    age = val >= 18 && val;
  };

  this.getAge = function() {
    return age;
  };

  this.isValid = function() {
    return !!age;
  };
};

Nå kan prototype-strukturen brukes som følger:

Ulike voksne har forskjellige aldre, men alle de voksne får de samme rettighetene.
Så, vi legger det til ved å bruke prototype, i stedet for this.

AdultPerson.prototype.getRights = function() {
  // Should be valid
  return this.isValid() && ['Booze', 'Drive'];
};

La oss se på implementeringen nå.

var p1 = new AdultPerson;
p1.setAge(12); // ( age = false )
console.log(p1.getRights()); // false ( Kid alert! )
p1.setAge(19); // ( age = 19 )
console.log(p1.getRights()); // ['Booze', 'Drive'] ( Welcome AdultPerson )

var p2 = new AdultPerson;
p2.setAge(45);    
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it ***

Håper dette hjelper.

Svar #9

Jeg vet at dette har blitt svart på til det kjedsommelige, men jeg vil gjerne vise et faktisk eksempel på hastighetsforskjeller.

Funksjon direkte på objektet:

function ExampleFn() {
    this.print = function() {
        console.log("Calling print! ");
    }
}

var objects = [];
console.time('x');
for (let i = 0; i < 2000000; i++) {
    objects.push(new ExampleFn());
}
console.timeEnd('x');

//x: 1151.960693359375ms

Funksjon på prototype:

function ExampleFn() {
}
ExampleFn.prototype.print = function() {
    console.log("Calling print!");
}

var objects = [];
console.time('y');
for (let i = 0; i < 2000000; i++) {
    objects.push(new ExampleFn());
}
console.timeEnd('y');

//x: 617.866943359375ms

Her lager vi 2 000 000 nye objekter med en print-metode i Chrome. Vi lagrer hvert objekt i et array. Å sette print på prototypen tar omtrent halvparten av tiden.

Svar #10

Prototype er malen til klassen; som gjelder for alle fremtidige forekomster av den. Mens dette er den spesifikke forekomsten av objektet.

Svar #11

La meg gi deg et mer omfattende svar som jeg lærte under et JavaScript-kurs.

De fleste svar nevnte forskjellen allerede, altså når man lager en prototype deles funksjonen med alle (fremtidige) instanser. Mens å erklære funksjonen i klassen vil lage en kopi for hver instans.

Generelt sett finnes det ikke noe riktig eller galt, det er mer et spørsmål om smak eller en designbeslutning avhengig av dine behov. Prototypen er derimot teknikken som brukes for å utvikle på en objektorientert måte, som jeg håper du vil se på slutten av dette svaret.

Du viste to mønstre i spørsmålet ditt. Jeg vil prøve å forklare to til og forsøke å forklare forskjellene hvis det er relevant. Føl deg fri til å redigere/utvide. I alle eksempler handler det om et bilobjekt som har en plassering og kan bevege seg.

Objektdekoratørmønster

Ikke sikker på om dette mønsteret fortsatt er relevant i dag, men det eksisterer. Og det er godt å vite om det. Du sender rett og slett et objekt og en egenskap til dekoratorfunksjonen. Dekoratoren returnerer objektet med egenskapen og metoden.

var carlike = function(obj, loc) {
    obj.loc = loc;
    obj.move = function() {
        obj.loc++;
    };
    return obj;
};

var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();

Funksjonelle klasser

En funksjon i JavaScript er et spesialisert objekt. I tillegg til å bli kalt, kan en funksjon lagre egenskaper som ethvert annet objekt.

I dette tilfellet er Car en funksjon (tenk også objekt) som kan kalles slik du er vant til. Den har en egenskap methods (som er et objekt med en move-funksjon). Når Car kalles, blir extend-funksjonen utført, som gjør litt magi, og utvider Car-funksjonen (tenk objekt) med metodene definert innenfor methods.

Dette eksemplet, selv om det er annerledes, kommer nærmest til det første eksemplet i spørsmålet.

var Car = function(loc) {
    var obj = {loc: loc};
    extend(obj, Car.methods);
    return obj;
};

Car.methods = {
    move : function() {
        this.loc++;
    }
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

Prototypiske klasser

De to første mønstrene tillater en diskusjon om bruk av teknikker for å definere delte metoder eller bruk av metoder som er definert direkte i kroppen til konstruktøren. I begge tilfeller har hver instans sin egen move-funksjon.

Den prototypiske modellen egner seg ikke godt for samme undersøkelse, fordi funksjonsdeling via en prototypedelegasjon er selve målet med den prototypiske modellen. Som andre påpekte, forventes det å ha et bedre minneavtrykk.

Det er imidlertid ett interessant punkt å vite: Hver prototype-objekt har en praktisk egenskap constructor, som peker tilbake til funksjonen (tenk objekt) det var knyttet til.

Når det gjelder de siste tre linjene:

I dette eksemplet lenker Car til prototype-objektet, som via constructor lenker til selve Car, dvs. Car.prototype.constructor er selve Car. Dette lar deg finne ut hvilken konstruksjonsfunksjon som bygde et bestemt objekt.

amy.constructor sin oppslag feiler og blir derfor delegert til Car.prototype, som har constructor-egenskapen. Og dermed er amy.constructor Car.

Videre er amy en instanceof Car. instanceof-operatoren fungerer ved å se om høyre operands prototype-objekt (Car) kan finnes hvor som helst i venstre operands prototype-kjede (amy).

var Car = function(loc) {
    var obj = Object.create(Car.prototype);
    obj.loc = loc;
    return obj;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

console.log(Car.prototype.constructor);
console.log(amy.constructor);
console.log(amy instanceof Car);

Noen utviklere kan være forvirret i begynnelsen. Se eksempelet nedenfor:

var Dog = function() {
  return {legs: 4, bark: alert};
};

var fido = Dog();
console.log(fido instanceof Dog);

Operatoren instanceof returnerer false, fordi Dogs prototype ikke kan finnes noe sted i fidos prototypekjede. fido er et enkelt objekt som er opprettet med en objektliteral, dvs. det delegerer bare til Object.prototype.

Pseudoklassiske mønstre

Dette er egentlig bare en annen form for det prototypiske mønsteret i forenklet form og mer kjent for de som programmerer i Java for eksempel, siden det bruker new-konstruktøren.

Det gjør det samme som i det prototypiske mønsteret egentlig, det er bare syntaktisk sukker på toppen av det prototypiske mønsteret.

Imidlertid er den primære forskjellen at det finnes optimaliseringer implementert i JavaScript-motorer som bare gjelder når man bruker det pseudoklassiske mønsteret. Tenk på det pseudoklassiske mønsteret som en sannsynlig raskere versjon av det prototypiske mønsteret; objektforholdene i begge eksemplene er de samme.

var Car = function(loc) {
    this.loc = loc;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = new Car(1);
amy.move();
var ben = new Car(9);
ben.move();

Til slutt bør det ikke være for vanskelig å forstå hvordan objektorientert programmering kan gjøres. Det er to seksjoner.

En seksjon som definerer vanlige egenskaper/metoder i prototypen (kjede).

Og en annen seksjon der du legger inn definisjonene som skiller objektene fra hverandre (loc-variabelen i eksemplene).

Dette er det som gjør at vi kan bruke konsepter som superklasse eller underklasse i JavaScript.

Føl deg fri til å legge til eller redigere. Når det er mer fullstendig, kan jeg kanskje gjøre dette til en fellesskapswiki.

Svar #12

Jeg tror at @Matthew Crumley har rett. De er funksjonelt, om ikke strukturelt, ekvivalente. Hvis du bruker Firebug for å se på objektene som opprettes med new, kan du se at de er de samme. Min preferanse ville imidlertid være følgende. Jeg antar at det bare føles mer som det jeg er vant til i C#/Java. Det vil si, definere klassen, definere feltene, konstruktøren og metodene.

var A = function() {};
A.prototype = {
    _instance_var: 0,

    initialize: function(v) { this._instance_var = v; },

    x: function() {  alert(this._instance_var); }
};

REDIGER Mente ikke å antyde at omfanget til variabelen var privat, jeg prøvde bare å illustrere hvordan jeg definerer klassene mine i javascript. Variabelnavnet har blitt endret for å reflektere dette.

Svar #13

Som diskutert i andre svar, er det egentlig en ytelsesvurdering fordi funksjonen i prototypen deles med alle instansene - i stedet for at funksjonen opprettes for hver instans.

Jeg satte sammen en jsperf for å vise dette. Det er en dramatisk forskjell i tiden det tar å instansiere klassen, selv om det egentlig bare er relevant hvis du lager mange forekomster.

http://jsperf.com/functions-in-constructor-vs-prototype

Svar #14

Tenk på statisk typet språk, ting på prototype er statiske og ting på this er instansrelaterte.

Relaterte artikler