Põhjalik sukeldumine JavaScripti prototüüpide ahelasse, uurides päri mustreid ja objektide globaalset loomist.
JavaScripti prototüüpide ahela lahtiharutamine: päri mustrid vs. objektide loomine
JavaScript, keel, mis toidab suurt osa kaasaegsest veebist ja selle ümbrusest, üllatab arendajaid sageli oma ainulaadse lähenemisega objektorienteeritud programmeerimisele. Erinevalt paljudest klassikalistest keeltest, mis tuginevad klassipõhisele pärimisele, kasutab JavaScript prototüüpide süsteemi. Selle süsteemi keskmes on prototüüpide ahel, põhimõiste, mis määrab, kuidas objektid pärivad omadusi ja meetodeid. Prototüüpide ahela mõistmine on JavaScripti valdamiseks hädavajalik, võimaldades arendajatel kirjutada tõhusamat, organiseeritumat ja vastupidavamat koodi. See artikkel demüstifitseerib selle võimsa mehhanismi, uurides selle rolli nii objektide loomisel kui ka päri mustrites.
JavaScripti objektimudeli kese: prototüübid
Enne ahelasse süvenemist on oluline mõista JavaScripti prototüübi mõistet. Igal JavaScripti objektil on selle loomisel sisemine link teisele objektile, mida nimetatakse selle prototüübiks. See link ei ole otseselt objektil endal avalik omadus, vaid on ligipääsetav spetsiaalse atribuudi __proto__
kaudu (kuigi see on pärand ja otsest manipuleerimist sageli ei soovitata) või usaldusväärsemalt Object.getPrototypeOf(obj)
kaudu.
Mõtle prototüübile kui ehitusplaanile või mallile. Kui üritate objektilt omadust või meetodit kasutada ja seda sellelt objektilt otseselt ei leita, ei anna JavaScript kohe viga. Selle asemel järgib see sisemist linki objekti prototüübini ja kontrollib seal. Kui see leitakse, kasutatakse omadust või meetodit. Kui ei, jätkatakse ahelas ülespoole, kuni jõutakse ülima esivanema, Object.prototype
-ni, mis lõpuks lingib null
-i.
Konstruktorid ja prototüübi atribuut
Üks levinumaid viise objektide loomiseks, mis jagavad ühist prototüüpi, on konstruktorfunktsioonide kasutamine. Konstruktorfunktsioon on lihtsalt funktsioon, mida kutsutakse välja new
-märksõnaga. Kui funktsioon on deklareeritud, saab see automaatselt atribuudi nimega prototype
, mis on ise objekt. See prototype
-objekt on see, mis määratakse prototüübiks kõigile selle funktsiooni abil konstruktorina loodud objektidele.
Vaadake seda näidet:
function Person(name, age) {
this.name = name;
this.age = age;
}
// Meetodi lisamine Personi prototüübile
Person.prototype.greet = function() {
console.log(`Tere, minu nimi on ${this.name} ja ma olen ${this.age} aastat vana.`);
};
const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);
person1.greet(); // Väljund: Tere, minu nimi on Alice ja ma olen 30 aastat vana.
person2.greet(); // Väljund: Tere, minu nimi on Bob ja ma olen 25 aastat vana.
Selles väljavõttes:
Person
on konstruktorfunktsioon.- Kui kutsutakse
new Person('Alice', 30)
, luuakse uus tühi objekt. Person
sees olevthis
-märksõna viitab sellele uuele objektile ning sellename
- jaage
-omadused seatakse.- Kriitiliselt on selle uue objekti
[[Prototype]]
sisemine atribuut seatud väärtuselePerson.prototype
. - Kui kutsutakse
person1.greet()
, otsib JavaScriptperson1
-ltgreet
atribuuti. Seda seal ei leidu. Seejärel otsib seeperson1
-i prototüüpi, mis onPerson.prototype
. Siin leitaksegreet
ja seda täidetakse.
See mehhanism võimaldab mitmel samalt konstruktorilt loodud objektil jagada samu meetodeid, mis viib mäluefektiivsuseni. Selle asemel, et igal objektil oleks oma koopia greet
-funktsioonist, viitavad nad kõik ühele funktsiooni eksemplarile prototüübil.
Prototüüpide ahel: pärimise hierarhia
Mõiste "prototüüpide ahel" viitab objektide järjestusele, mida JavaScript läbib, otsides omadust või meetodit. Igal JavaScripti objektil on link oma prototüübi külge ja sellel prototüübil on omakorda link oma prototüübi külge ja nii edasi. See loob pärimisahela.
Ahel lõpeb, kui objekti prototüüp on null
. Selle ahela kõige levinum juur on Object.prototype
, mille prototüüp on ise null
.
Visualiseerime ahela meie Person
-näitest:
person1
→ Person.prototype
→ Object.prototype
→ null
Kui te näiteks kasutate person1.toString()
:
- JavaScript kontrollib, kas
person1
-l ontoString
-omadus. Seda pole. - See kontrollib
Person.prototype
-itoString
-i osas. Seda ei leidu seal otse. - See liigub ülespoole
Object.prototype
-ni. Siin ontoString
määratletud ja seda on võimalik kasutada.
See läbimise mehhanism on JavaScripti prototüüpide põhise pärimise olemus. See on dünaamiline ja paindlik, võimaldades ahela tööaja muutmist.
`Object.create()` mõistmine
Kuigi konstruktorfunktsioonid on prototüüpsuhete loomise populaarne viis, pakub Object.create()
meetod otsesemat ja selgemat viisi uute objektide loomiseks määratud prototüübiga.
Object.create(proto, [propertiesObject])
:
proto
: objekt, mis saab uue loodud objekti prototüübiks.propertiesObject
(valikuline): objekt, mis määratleb täiendavad omadused, mis lisatakse uuele objektile.
Näide Object.create()
kasutamisest:
const animalPrototype = {
speak: function() {
console.log(`${this.name} teeb häält.`);
}
};
const dog = Object.create(animalPrototype);
dog.name = 'Buddy';
dog.speak(); // Väljund: Buddy teeb häält.
const cat = Object.create(animalPrototype);
cat.name = 'Whiskers';
cat.speak(); // Väljund: Whiskers teeb häält.
Sellisel juhul:
animalPrototype
on objektiloom, mis toimib ehitusplaanina.Object.create(animalPrototype)
loob uue objekti (dog
), mille[[Prototype]]
sisemine atribuut on seatud väärtuseleanimalPrototype
.dog
-l endalspeak
-meetodit pole, kuid ta pärib selleanimalPrototype
-lt.
See meetod on eriti kasulik objektide loomiseks, mis pärivad teistelt objektidelt ilma konstruktorfunktsiooni kasutamata, pakkudes üksikasjalikumat kontrolli pärimiskorralduse üle.
Pärimismustrid JavaScriptis
Prototüüpide ahel on alus, millele on ehitatud erinevad pärimismustrid JavaScriptis. Kuigi kaasaegne JavaScript pakub class
süntaksit (tutvustati ES6/ECMAScript 2015), on oluline meeles pidada, et see on suuresti süntaktiline suhkur olemasoleva prototüübipõhise pärimise kohal.
1. Prototüüpiline pärimine (alus)
Nagu arutati, on see põhiprotseduur. Objektid pärivad otse teistelt objektidelt. Konstruktorfunktsioonid ja Object.create()
on peamised tööriistad nende suhete loomiseks.
2. Konstruktori varastamine (või delegeerimine)
Seda mustrit kasutatakse sageli siis, kui soovite pärida baaskonstruktorilt, kuid soovite määratleda meetodeid tuletatud konstruktori prototüübil. Vanemkonstrutori omaduste kopeerimiseks kutsute lapsekonstruktoris vanemkonstrutori call()
või apply()
abil.
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function() {
console.log(`${this.name} liigub.`);
};
function Dog(name, breed) {
Animal.call(this, name); // Konstruktori varastamine
this.breed = breed;
}
// Prototüüpide ahela seadistamine pärimiseks
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // Konstruktori viite lähtestamine
Dog.prototype.bark = function() {
console.log(`${this.name} haugub! Vuh!`);
};
const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.move(); // Päritud Animal.prototype-lt
myDog.bark(); // Määratletud Dog.prototype-l
console.log(myDog.name); // Päritud Animal.call-lt
console.log(myDog.breed);
Selles mustris:
Animal
on baaskonstruktor.Dog
on tuletatud konstruktor.Animal.call(this, name)
täidabAnimal
-i konstruktori praeguseDog
-i eksemplariga kuithis
, kopeeridesname
-omaduse.Dog.prototype = Object.create(Animal.prototype)
seab prototüüpide ahela, muutesAnimal.prototype
väärtuseksDog.prototype
prototüübi.Dog.prototype.constructor = Dog
on oluline konstruktori viite parandamiseks, mis pärast pärimise seadistamist muidu osutaksAnimal
-ile.
3. Parasüütiline kombinatsioonipärimine (vanema JS-i parim tava)
See on robustne muster, mis ühendab konstruktori varastamise ja prototüüpide pärimise täieliku prototüüpide pärimise saavutamiseks. Seda peetakse üheks kõige tõhusamaks meetodiks enne ES6 klasse.
function Parent(name) {
this.name = name;
}
Parent.prototype.getParentName = function() {
return this.name;
};
function Child(name, age) {
Parent.call(this, name); // Konstruktori varastamine
this.age = age;
}
// Prototüüpide pärimine
const childProto = Object.create(Parent.prototype);
childProto.getChildAge = function() {
return this.age;
};
Child.prototype = childProto;
Child.prototype.constructor = Child;
const myChild = new Child('Alice', 10);
console.log(myChild.getParentName()); // Alice
console.log(myChild.getChildAge()); // 10
See muster tagab, et nii vanemkonstrutori omadused (call
-i kaudu) kui ka vanemprototüübi meetodid (Object.create
kaudu) päritakse õigesti.
4. ES6 Klassid: süntaktiline suhkur
ES6 tutvustas class
-märksõna, mis pakub puhtamat, tuttavamat süntaksit klassipõhistest keeltest pärit arendajatele. Kuid selle all kasutab see ikkagi prototüüpide ahelat.
class Animal {
constructor(name) {
this.name = name;
}
move() {
console.log(`${this.name} liigub.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Kutsub vanemkonstrutori
this.breed = breed;
}
bark() {
console.log(`${this.name} haugub! Vuh!`);
}
}
const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.move(); // Päritud
myDog.bark(); // Määratletud Dog-is
Selles ES6 näites:
class
-märksõna määratleb ehitusplaani.constructor
-meetod on eriline ja seda kutsutakse välja, kui uus eksemplar luuakse.extends
-märksõna loob prototüüpide ahela lingi.super()
lapsekonstruktoris on samaväärneParent.call()
-ga, tagades, et vanemkonstuktor kutsutakse välja.
class
-süntaks muudab koodi loetavamaks ja hooldatavamaks, kuid on elutähtis meeles pidada, et alusmehhanismiks jääb prototüübipõhine pärimine.
Objektide loomise meetodid JavaScriptis
Lisaks konstruktorfunktsioonidele ja ES6 klassidele pakub JavaScript mitmeid viise objektide loomiseks, millest igaühel on mõju nende prototüüpide ahelale:
- Objektiloomid: Kõige tavalisem viis üksikute objektide loomiseks. Nendel objektidel on nende otsene prototüüp
Object.prototype
. new Object()
: Sarnaselt objektiloomudega loob objekti, mille prototüüp onObject.prototype
. Üldiselt vähem kompaktne kui objektiloomud.Object.create()
: Nagu eespool üksikasjalikult kirjeldatud, võimaldab otsest kontrolli uue loodud objekti prototüübi üle.- Konstruktorfunktsioonid koos
new
-iga: Loob objekte, mille prototüüp on konstruktorfunktsiooniprototype
atribuut. - ES6 Klassid: Süntaktiline suhkur, mis lõpptulemusena loob objekte, mille prototüübid on allpool lingitud
Object.create()
abil. - Tehasefunktsioonid: Funktsioonid, mis tagastavad uusi objekte. Nende objektide prototüüp sõltub sellest, kuidas need tehasefunktsiooni sees luuakse. Kui need luuakse objektiloomude või
Object.create()
abil, seatakse nende prototüübid vastavalt.
const myObject = { key: 'value' };
// myObjecti prototüüp on Object.prototype
console.log(Object.getPrototypeOf(myObject) === Object.prototype); // true
const anotherObject = new Object();
anotherObject.name = 'Test';
// anotherObjecti prototüüp on Object.prototype
console.log(Object.getPrototypeOf(anotherObject) === Object.prototype); // true
function createPerson(name, age) {
return {
name: name,
age: age,
greet: function() {
console.log(`Tere, ma olen ${this.name}`);
}
};
}
const factoryPerson = createPerson('Charles', 40);
// Prototüüp on siin vaikimisi ikkagi Object.prototype.
// Pärimiseks tuleks tehases kasutada Object.create.
console.log(Object.getPrototypeOf(factoryPerson) === Object.prototype); // true
Praktilised tagajärjed ja globaalsed parimad tavad
Prototüüpide ahela mõistmine pole lihtsalt akadeemiline harjutus; sellel on märkimisväärsed praktilised tagajärjed jõudlusele, mäluhaldusse ja koodi korraldamisele erinevates globaalsetes arendusmeeskondades.
Jõudluse kaalutlused
- Jagatud meetodid: Meetodite paigutamine prototüübile (vastupidiselt igale eksemplarile) säästab mälu, kuna meetodist on ainult üks koopia. See on eriti oluline suuremahulistes rakendustes või ressursside poolest piiratud keskkondades.
- Otsingu aeg: Kuigi tõhus, võib pika prototüüpide ahela läbimine tekitada väikese jõudlusülekulu. Äärmuslikel juhtudel võivad sügavad pärimisahelad olla vähem tõhusad kui lamedamad. Arendajad peaksid püüdma mõistliku sügavuse poole.
- Vahemällu salvestamine: Kui kasutatakse sageli kasutatavaid omadusi või meetodeid, salvestavad JavaScripti mootorid nende asukohad hilisemaks kiiremaks juurdepääsuks.
Mäluhaldus
Nagu mainitud, on prototüüpide kaudu meetodite jagamine peamine mälutöötluse optimeerimine. Mõelge olukorrale, kus miljonid identsed nupukomponendid renderdatakse veebilehel erinevates piirkondades. Iga nupueksemplar, mis jagab ühte prototüübile määratletud onClick
-käivitust, on märkimisväärselt mäluefektiivsem kui iga nupu oma funktsiooni eksemplariga.
Koodi korraldamine ja hooldatavus
Prototüüpide ahel võimaldab teie koodi selget ja hierarhilist struktuuri, edendades korduskasutamist ja hooldatavust. Arendajad üle kogu maailma saavad järgida väljakujunenud mustreid, nagu ES6 klasside või hästi määratletud konstruktorfunktsioonide kasutamine, et luua ettearvatavaid pärimisstruktuure.
Prototüüpide silumine
Tööriistad nagu brauseri arendajakonsoolid on prototüüpide ahela kontrollimiseks hindamatud. Tavaliselt näete __proto__
linki või kasutate Object.getPrototypes()
, et visualiseerida ahelat ja mõista, kust omadusi päritakse.
Globaalsed näited:
- Rahvusvahelised e-kaubanduse platvormid: Globaalsel e-kaubanduse saidil võib olla baas
Product
klass. Erinevad toote tüübid (ntElectronicsProduct
,ClothingProduct
,GroceryProduct
) pärivadProduct
-lt. Iga spetsialiseeritud toode võib üle kirjutada või lisada oma kategooriale vastavaid meetodeid (ntcalculateShippingCost()
elektroonika jaoks,checkExpiryDate()
toidukaupade jaoks). Prototüüpide ahel tagab, et ühised toote atribuudid ja käitumised korduskasutatakse tõhusalt kõigi toote tüüpide ja kõikide riikide kasutajate jaoks. - Globaalsed sisuhaldussüsteemid (CMS): Ülemaailmselt organisatsioonide poolt kasutatav CMS võib omada baas
ContentItem
. Siis pärivad tüübid naguArticle
,Page
,Image
sellest.Article
-l võivad olla spetsiifilised meetodid SEO optimeerimiseks, mis on vastavad erinevatele otsingumootoritele ja keelte, samas kuiPage
võib keskenduda paigutusele ja navigeerimisele, kõik kasutavad põhiliste sisufunktsioonide jaoks ühist prototüüpide ahelat. - Platvormideülesed mobiilirakendused: Raamistikud nagu React Native võimaldavad arendajatel luua iOS-i ja Androidi rakendusi ühelt koodibaasilt. Aluseline JavaScripti mootor ja selle prototüübisüsteem on selle koodi taaskasutamise võimaldamisel olulised, kusjuures komponendid ja teenused on sageli paigutatud pärimisstruktuuri, mis toimib identselt erinevates seadme ökosüsteemides ja kasutajaskondades.
Vältimisväärsed levinud ohud
Kuigi võimas, võib prototüüpide ahel segadust tekitada, kui seda täielikult ei mõisteta:
Object.prototype
otsene muutmine: See on globaalne muudatus, mis võib rikkuda teisi teeke või koodi, mis tuginebObject.prototype
vaikimisi käitumisele. Seda ei soovitata tungivalt.- Konstruktori valesti lähtestamine: Prototüüpide ahelate käsitsi seadistamisel (nt
Object.create()
kasutades) veenduge, etconstructor
-omadus oleks õigesti suunatud tagasi soovitud konstruktorfunktsioonile. super()
unustamine ES6 klassides: Kui tuletatud klassil on konstruktor ja ta ei kutsuthis
-i kasutamisele eelnevaltsuper()
, põhjustab see tööaja viga.prototype
ja__proto__
(võiObject.getPrototypeOf()
) segadusse ajamine:prototype
on konstruktorfunktsiooni atribuut, mis saab eksemplari prototüübiks.__proto__
(võiObject.getPrototypeOf()
) on sisemine link eksemplarist selle prototüübi külge.
Järeldus
JavaScripti prototüüpide ahel on keele objektimudeli nurgakivi. See pakub paindlikku ja dünaamilist mehhanismi pärimiseks ja objektide loomiseks, toetades kõike lihtsatest objektiloomudest keerukate klassihierarhiateni. Prototüüpide, konstruktorfunktsioonide, Object.create()
ning ES6 klasside aluspõhimõtete mõistmisega saavad arendajad kirjutada tõhusamat, skaleeritavamat ja hooldatavamat koodi. Tugev arusaam prototüüpide ahelast annab arendajatele võimaluse luua keerukaid rakendusi, mis toimivad usaldusväärselt kogu maailmas, tagades järjepidevuse ja korduskasutamise erinevates tehnoloogilistes maastikes.
Ükskõik, kas töötate vanema JavaScripti koodiga või kasutate uusimaid ES6+ funktsioone, jääb prototüüpide ahel oluliseks mõisteks, mis tuleb haarata igal tõsisel JavaScripti arendajal. See on vaikne mootor, mis juhib objektide suhteid, võimaldades luua võimsaid ja dünaamilisi rakendusi, mis toidavad meie omavahel ühendatud maailma.