PÔhjalik juhend JavaScripti klassipÀriluse kohta, mis uurib erinevaid mustreid ja parimaid praktikaid robustsete ning hooldatavate rakenduste loomiseks.
JavaScripti objektorienteeritud programmeerimine: klassipÀriluse mustrite valdamine
Objektorienteeritud programmeerimine (OOP) on vĂ”imas paradigma, mis vĂ”imaldab arendajatel oma koodi struktureerida modulaarsel ja taaskasutataval viisil. PĂ€rilus, OOP-i pĂ”hikontseptsioon, vĂ”imaldab meil luua uusi klasse olemasolevate pĂ”hjal, pĂ€rides nende omadused ja meetodid. See soodustab koodi taaskasutamist, vĂ€hendab liiasust ja parandab hooldatavust. JavaScriptis saavutatakse pĂ€rilus erinevate mustrite kaudu, millest igaĂŒhel on oma eelised ja puudused. See artikkel pakub pĂ”hjalikku ĂŒlevaadet nendest mustritest, alates traditsioonilisest prototĂŒĂŒpsest pĂ€rilusest kuni kaasaegsete ES6 klasside ja kaugemalegi.
PĂ”hitĂ”dede mĂ”istmine: prototĂŒĂŒbid ja prototĂŒĂŒbiahel
SĂŒgaval sisimas pĂ”hineb JavaScripti pĂ€rilusmudel prototĂŒĂŒpidel. Igal JavaScripti objektil on sellega seotud prototĂŒĂŒpobjekt. Kui proovite pÀÀseda juurde objekti omadusele vĂ”i meetodile, otsib JavaScript seda esmalt otse objektilt endalt. Kui seda ei leita, otsib see seejĂ€rel objekti prototĂŒĂŒpi. See protsess jĂ€tkub mööda prototĂŒĂŒbiahelat, kuni omadus leitakse vĂ”i ahela lĂ”puni jĂ”utakse (mis on tavaliselt `null`).
See prototĂŒĂŒpne pĂ€rilus erineb klassikalisest pĂ€rilusest, mida leidub keeltes nagu Java vĂ”i C++. Klassikalises pĂ€riluses pĂ€rivad klassid otse teistest klassidest. PrototĂŒĂŒpses pĂ€riluses pĂ€rivad objektid otse teistest objektidest (vĂ”i tĂ€psemalt, nende objektidega seotud prototĂŒĂŒpobjektidest).
`__proto__` omadus (iganenud, kuid mÔistmiseks oluline)
Kuigi ametlikult iganenud, pakub `__proto__` omadus (topelt allkriips proto topelt allkriips) otsest viisi objekti prototĂŒĂŒbile juurdepÀÀsemiseks. Kuigi te ei tohiks seda tootmiskoodis kasutada, aitab selle mĂ”istmine prototĂŒĂŒbiahelat visualiseerida. NĂ€iteks:
const animal = {
name: 'Generic Animal',
makeSound: function() {
console.log('Generic sound');
}
};
const dog = {
name: 'Dog',
breed: 'Golden Retriever'
};
dog.__proto__ = animal; // Sets animal as the prototype of dog
console.log(dog.name); // Output: Dog (dog has its own name property)
console.log(dog.breed); // Output: Golden Retriever
console.log(dog.makeSound()); // Output: Generic sound (inherited from animal)
Selles nĂ€ites pĂ€rib `dog` meetodi `makeSound` objektilt `animal` prototĂŒĂŒbiahela kaudu.
`Object.getPrototypeOf()` ja `Object.setPrototypeOf()` meetodid
Need on eelistatud meetodid vastavalt objekti prototĂŒĂŒbi saamiseks ja seadistamiseks, pakkudes standardsemat ja usaldusvÀÀrsemat lĂ€henemist vĂ”rreldes `__proto__`-ga. Kaaluge nende meetodite kasutamist prototĂŒĂŒbi suhete haldamiseks.
const animal = {
name: 'Generic Animal',
makeSound: function() {
console.log('Generic sound');
}
};
const dog = {
name: 'Dog',
breed: 'Golden Retriever'
};
Object.setPrototypeOf(dog, animal);
console.log(dog.name); // Output: Dog
console.log(dog.breed); // Output: Golden Retriever
console.log(dog.makeSound()); // Output: Generic sound
console.log(Object.getPrototypeOf(dog) === animal); // Output: true
Klassikalise pĂ€riluse simulatsioon prototĂŒĂŒpidega
Kuigi JavaScriptis pole klassikalist pĂ€rilust samal viisil kui mĂ”nes teises keeles, saame seda simuleerida konstruktorfunktsioonide ja prototĂŒĂŒpide abil. See lĂ€henemine oli levinud enne ES6 klasside kasutuselevĂ”ttu.
Konstruktorfunktsioonid
Konstruktorfunktsioonid on tavalised JavaScripti funktsioonid, mida kutsutakse vĂ€lja `new` mĂ€rksĂ”naga. Kui konstruktorfunktsioon kutsutakse vĂ€lja `new`-ga, loob see uue objekti, seab `this` viitama sellele objektile ja tagastab kaudselt uue objekti. Konstruktorfunktsiooni `prototype` omadust kasutatakse uue objekti prototĂŒĂŒbi mÀÀratlemiseks.
function Animal(name) {
this.name = name;
}
Animal.prototype.makeSound = function() {
console.log('Generic sound');
};
function Dog(name, breed) {
Animal.call(this, name); // Call the Animal constructor to initialize the name property
this.breed = breed;
}
// Set Dog's prototype to a new instance of Animal. This establishes the inheritance link.
Dog.prototype = Object.create(Animal.prototype);
// Correct the constructor property on Dog's prototype to point to Dog itself.
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log('Woof!');
};
const myDog = new Dog('Buddy', 'Labrador');
console.log(myDog.name); // Output: Buddy
console.log(myDog.breed); // Output: Labrador
console.log(myDog.makeSound()); // Output: Generic sound (inherited from Animal)
console.log(myDog.bark()); // Output: Woof!
console.log(myDog instanceof Animal); // Output: true
console.log(myDog instanceof Dog); // Output: true
Selgitus:
- `Animal.call(this, name)`: See rida kutsub vÀlja `Animal` konstruktori `Dog` konstruktori sees, seades `name` omaduse uuele `Dog` objektile. See on viis, kuidas me initsialiseerime vanemklassis mÀÀratletud omadusi. `.call` meetod vÔimaldab meil kÀivitada funktsiooni konkreetse `this` kontekstiga.
- `Dog.prototype = Object.create(Animal.prototype)`: See on pĂ€riluse seadistamise tuum. `Object.create(Animal.prototype)` loob uue objekti, mille prototĂŒĂŒbiks on `Animal.prototype`. SeejĂ€rel omistame selle uue objekti `Dog.prototype`-le. See loob pĂ€rilussuhte: `Dog` isendid pĂ€rivad omadusi ja meetodeid `Animal`-i prototĂŒĂŒbilt.
- `Dog.prototype.constructor = Dog`: PĂ€rast prototĂŒĂŒbi seadistamist viitab `Dog.prototype`-i `constructor` omadus ekslikult `Animal`-ile. Peame selle lĂ€htestama, et see viitaks `Dog`-ile endale. See on oluline `Dog` isendite konstruktori Ă”igeks tuvastamiseks.
- `instanceof`: Operaator `instanceof` kontrollib, kas objekt on konkreetse konstruktorfunktsiooni (vĂ”i selle prototĂŒĂŒbiahela) isend.
Miks `Object.create`?
`Object.create(Animal.prototype)` kasutamine on ĂŒlioluline, sest see loob uue objekti ilma `Animal` konstruktorit vĂ€lja kutsumata. Kui me kasutaksime `new Animal()`, looksime tahtmatult `Animal` isendi pĂ€riluse seadistamise osana, mida me ei soovi. `Object.create` pakub puhast viisi prototĂŒĂŒpse sideme loomiseks ilma soovimatute kĂ”rvalmĂ”judeta.
ES6 klassid: sĂŒntaktiline suhkur prototĂŒĂŒpsele pĂ€rilusele
ES6 (ECMAScript 2015) tĂ”i sisse `class` mĂ€rksĂ”na, pakkudes tuttavamat sĂŒntaksit klasside ja pĂ€riluse mÀÀratlemiseks. Siiski on oluline meeles pidada, et ES6 klassid pĂ”hinevad endiselt kapoti all prototĂŒĂŒpsel pĂ€rilusel. Need pakuvad mugavamat ja loetavamat viisi prototĂŒĂŒpidega töötamiseks.
class Animal {
constructor(name) {
this.name = name;
}
makeSound() {
console.log('Generic sound');
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Call the Animal constructor
this.breed = breed;
}
bark() {
console.log('Woof!');
}
}
const myDog = new Dog('Buddy', 'Labrador');
console.log(myDog.name); // Output: Buddy
console.log(myDog.breed); // Output: Labrador
console.log(myDog.makeSound()); // Output: Generic sound
console.log(myDog.bark()); // Output: Woof!
console.log(myDog instanceof Animal); // Output: true
console.log(myDog instanceof Dog); // Output: true
Selgitus:
- `class Animal { ... }`: MÀÀratleb klassi nimega `Animal`.
- `constructor(name) { ... }`: MÀÀratleb `Animal` klassi konstruktori.
- `extends Animal`: NÀitab, et `Dog` klass pÀrib `Animal` klassist.
- `super(name)`: Kutsub vÀlja vanemklassi (`Animal`) konstruktori, et initsialiseerida `name` omadus. `super()` tuleb vÀlja kutsuda enne `this` kasutamist tuletatud klassi konstruktoris.
ES6 klassid pakuvad puhtamat ja lĂŒhemat sĂŒntaksit objektide loomiseks ja pĂ€rilussuhete haldamiseks, muutes koodi lihtsamini loetavaks ja hooldatavaks. `extends` mĂ€rksĂ”na lihtsustab alamklasside loomise protsessi ja `super()` mĂ€rksĂ”na pakub otsekohest viisi vanemklassi konstruktori ja meetodite vĂ€ljakutsumiseks.
Meetodi ĂŒledefineerimine
Nii klassikaline simulatsioon kui ka ES6 klassid vĂ”imaldavad teil ĂŒle defineerida vanemklassist pĂ€ritud meetodeid. See tĂ€hendab, et saate pakkuda meetodi spetsialiseeritud implementatsiooni alamklassis.
class Animal {
constructor(name) {
this.name = name;
}
makeSound() {
console.log('Generic sound');
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
makeSound() {
console.log('Woof!'); // Overriding the makeSound method
}
bark() {
console.log('Woof!');
}
}
const myDog = new Dog('Buddy', 'Labrador');
myDog.makeSound(); // Output: Woof! (Dog's implementation)
Selles nĂ€ites defineerib `Dog` klass ĂŒle `makeSound` meetodi, pakkudes oma implementatsiooni, mis vĂ€ljastab "Woof!".
Klassikalisest pÀrilusest kaugemale: alternatiivsed mustrid
Kuigi klassikaline pÀrilus on levinud muster, ei ole see alati parim lÀhenemine. MÔnel juhul pakuvad alternatiivsed mustrid nagu mixinid ja kompositsioon rohkem paindlikkust ja vÀldivad pÀriluse potentsiaalseid lÔkse.
Mixinid
Mixinid on viis funktsionaalsuse lisamiseks klassile ilma pÀrilust kasutamata. Mixin on klass vÔi objekt, mis pakub meetodite kogumit, mida saab teistesse klassidesse "sisse segada". See vÔimaldab teil koodi taaskasutada mitme klassi vahel ilma keerulist pÀrilushierarhiat loomata.
const barkMixin = {
bark() {
console.log('Woof!');
}
};
const flyMixin = {
fly() {
console.log('Flying!');
}
};
class Dog {
constructor(name) {
this.name = name;
}
}
class Bird {
constructor(name) {
this.name = name;
}
}
// Apply the mixins (using Object.assign for simplicity)
Object.assign(Dog.prototype, barkMixin);
Object.assign(Bird.prototype, flyMixin);
const myDog = new Dog('Buddy');
myDog.bark(); // Output: Woof!
const myBird = new Bird('Tweety');
myBird.fly(); // Output: Flying!
Selles nÀites pakub `barkMixin` meetodit `bark`, mis lisatakse `Dog` klassile, kasutades `Object.assign`. Sarnaselt pakub `flyMixin` meetodit `fly`, mis lisatakse `Bird` klassile. See vÔimaldab mÔlemal klassil omada soovitud funktsionaalsust, ilma et nad oleksid pÀriluse kaudu seotud.
Keerukamad mixin'ite implementatsioonid vĂ”ivad kasutada tehasefunktsioone vĂ”i dekoraatoreid, et pakkuda rohkem kontrolli segamisprotsessi ĂŒle.
Kompositsioon
Kompositsioon on veel ĂŒks alternatiiv pĂ€rilusele. Selle asemel, et pĂ€rida funktsionaalsust vanemklassist, vĂ”ib klass sisaldada teiste klasside isendeid komponentidena. See vĂ”imaldab teil ehitada keerulisi objekte, kombineerides lihtsamaid objekte.
class Engine {
start() {
console.log('Engine started');
}
}
class Wheels {
rotate() {
console.log('Wheels rotating');
}
}
class Car {
constructor() {
this.engine = new Engine();
this.wheels = new Wheels();
}
drive() {
this.engine.start();
this.wheels.rotate();
console.log('Car driving');
}
}
const myCar = new Car();
myCar.drive();
// Output:
// Engine started
// Wheels rotating
// Car driving
Selles nÀites koosneb `Car` klass objektidest `Engine` ja `Wheels`. Selle asemel, et nendest klassidest pÀrida, sisaldab `Car` klass nende isendeid ja kasutab nende meetodeid oma funktsionaalsuse implementeerimiseks. See lÀhenemine soodustab lÔdva sidususe pÔhimÔtet ja vÔimaldab suuremat paindlikkust erinevate komponentide kombineerimisel.
JavaScripti pÀriluse parimad praktikad
- Eelista kompositsiooni pÀrilusele: VÔimaluse korral eelista alati kompositsiooni pÀrilusele. Kompositsioon pakub rohkem paindlikkust ja vÀldib tihedat sidusust, mis vÔib pÀrilushierarhiatest tuleneda.
- Kasuta ES6 klasse: Kasuta ES6 klasse puhtama ja loetavama sĂŒntaksi saavutamiseks. Need pakuvad kaasaegsemat ja hooldatavamat viisi prototĂŒĂŒpse pĂ€rilusega töötamiseks.
- VĂ€ldi sĂŒgavaid pĂ€rilushierarhiaid: SĂŒgavad pĂ€rilushierarhiad vĂ”ivad muutuda keeruliseks ja raskesti mĂ”istetavaks. Hoia pĂ€rilushierarhiad madalad ja fokusseeritud.
- Kaalu mixin'ite kasutamist: Kasuta mixin'eid funktsionaalsuse lisamiseks klassidele ilma keerulisi pÀrilussuhteid loomata.
- MĂ”ista prototĂŒĂŒbiahelat: PrototĂŒĂŒbiahela pĂ”hjalik mĂ”istmine on JavaScripti pĂ€rilusega tĂ”husaks töötamiseks hĂ€davajalik.
- Kasuta `Object.create` Ă”igesti: Klassikalise pĂ€riluse simuleerimisel kasuta `Object.create(Parent.prototype)`, et luua prototĂŒĂŒbi suhe ilma vanema konstruktorit vĂ€lja kutsumata.
- Paranda konstruktori omadus: PĂ€rast prototĂŒĂŒbi seadistamist paranda `constructor` omadus alamklassi prototĂŒĂŒbil, et see viitaks alamklassi konstruktorile.
Globaalsed kaalutlused koodistiili osas
Globaalses meeskonnas töötades arvesta jÀrgmiste punktidega:
- JÀrjepidevad nimekonventsioonid: Kasuta selgeid ja jÀrjepidevaid nimekonventsioone, mis on kÔigile meeskonnaliikmetele kergesti arusaadavad, olenemata nende emakeelest.
- Koodikommentaarid: Kirjuta pÔhjalikke koodikommentaare, et selgitada oma koodi eesmÀrki ja funktsionaalsust. See on eriti oluline keeruliste pÀrilussuhete puhul. Kaalu dokumentatsiooni generaatori nagu JSDoc kasutamist API dokumentatsiooni loomiseks.
- Rahvusvahelistamine (i18n) ja lokaliseerimine (l10n): Kui sinu rakendus peab toetama mitut keelt, mĂ”tle, kuidas pĂ€rilus vĂ”ib mĂ”jutada sinu i18n ja l10n strateegiaid. NĂ€iteks vĂ”ib olla vajalik meetodeid alamklassides ĂŒle defineerida, et kĂ€sitleda erinevaid keelespetsiifilisi vormindamisnĂ”udeid.
- Testimine: Kirjuta pĂ”hjalikke ĂŒhikteste, et tagada sinu pĂ€rilussuhete korrektne toimimine ja et kĂ”ik ĂŒledefineeritud meetodid kĂ€ituksid ootuspĂ€raselt. Pööra tĂ€helepanu ÀÀrmusjuhtumite testimisele ja potentsiaalsetele jĂ”udlusprobleemidele.
- KoodiĂŒlevaatused: Vii lĂ€bi regulaarseid koodiĂŒlevaatusi, et tagada kĂ”igi meeskonnaliikmete parimate praktikate jĂ€rgimine ning et kood oleks hĂ€sti dokumenteeritud ja kergesti mĂ”istetav.
KokkuvÔte
JavaScripti pĂ€rilus on vĂ”imas tööriist taaskasutatava ja hooldatava koodi loomiseks. MĂ”istes erinevaid pĂ€rilusmustreid ja parimaid praktikaid, saad luua vastupidavaid ja skaleeritavaid rakendusi. ĂkskĂ”ik, kas valid klassikalise simulatsiooni, ES6 klassid, mixinid vĂ”i kompositsiooni, on vĂ”tmetĂ€htsusega valida muster, mis sobib kĂ”ige paremini sinu vajadustega, ja kirjutada koodi, mis on selge, lĂŒhike ja globaalsele publikule kergesti mĂ”istetav.