Avastage JavaScript'i dekoraatorid ja kuidas need võimaldavad metaandmetega programmeerimist, parandavad koodi taaskasutatavust ja rakenduste hooldatavust. Õppige praktiliste näidete abil.
JavaScript'i dekoraatorid: metaandmetega programmeerimise võimekuse avamine
JavaScript'i dekoraatorid, mis said standardfunktsiooniks ES2022-s, pakuvad võimsat ja elegantset viisi metaandmete lisamiseks ning klasside, meetodite, omaduste ja parameetrite käitumise muutmiseks. Need pakuvad deklaratiivset süntaksit läbivate aspektide rakendamiseks, mis viib hooldatavama, taaskasutatavama ja väljendusrikkama koodini. See blogipostitus süveneb JavaScript'i dekoraatorite maailma, uurides nende põhikontseptsioone, praktilisi rakendusi ja alusmehhanisme, mis panevad need tööle.
Mis on JavaScript'i dekoraatorid?
Oma olemuselt on dekoraatorid funktsioonid, mis muudavad või täiustavad dekoreeritud elementi. Nad kasutavad sümbolit @
, millele järgneb dekoraatori funktsiooni nimi. Mõelge neist kui annotatsioonidest või modifikaatoritest, mis lisavad metaandmeid või muudavad aluskäitumist, ilma et muudaksid otse dekoreeritud olemuse põhilist loogikat. Nad mähivad tõhusalt dekoreeritud elemendi, süstides kohandatud funktsionaalsust.
Näiteks võib dekoraator automaatselt logida meetodikutseid, valideerida sisendparameetreid või hallata juurdepääsukontrolli. Dekoraatorid edendavad ülesannete eraldamist, hoides põhilise äriloogika puhta ja fokusseerituna, võimaldades samal ajal lisada täiendavaid käitumisviise modulaarsel moel.
Dekoraatorite sĂĽntaks
Dekoraatoreid rakendatakse kasutades sĂĽmbolit @
enne elementi, mida nad dekoreerivad. On olemas erinevat tĂĽĂĽpi dekoraatoreid, millest igaĂĽks on suunatud konkreetsele elemendile:
- Klassidekoraatorid: Rakendatakse klassidele.
- Meetodidekoraatorid: Rakendatakse meetoditele.
- Omaduste dekoraatorid: Rakendatakse omadustele.
- Pöördusmeetodite (accessor) dekoraatorid: Rakendatakse get- ja set-meetoditele.
- Parameetrite dekoraatorid: Rakendatakse meetodite parameetritele.
Siin on klassidekoraatori põhinäide:
@logClass
class MyClass {
constructor() {
// ...
}
}
function logClass(target) {
console.log(`Class ${target.name} has been created.`);
}
Selles näites on logClass
dekoraatorifunktsioon, mis võtab argumendiks klassi konstruktori (target
). Seejärel logib see konsooli teate iga kord, kui luuakse MyClass
'i isend.
Metaandmetega programmeerimise mõistmine
Dekoraatorid on tihedalt seotud metaandmetega programmeerimise kontseptsiooniga. Metaandmed on "andmed andmete kohta". Programmeerimise kontekstis kirjeldavad metaandmed koodielementide, näiteks klasside, meetodite ja omaduste, tunnuseid ja omadusi. Dekoraatorid võimaldavad teil seostada metaandmeid nende elementidega, võimaldades käitusajal introspektsiooni ja käitumise muutmist nende metaandmete põhjal.
Reflect Metadata
API (osa ECMAScripti spetsifikatsioonist) pakub standardset viisi objektide ja nende omadustega seotud metaandmete määratlemiseks ja hankimiseks. Kuigi see pole rangelt vajalik kõigi dekoraatorite kasutusjuhtude jaoks, on see võimas tööriist keerukamate stsenaariumide jaoks, kus peate käitusajal dünaamiliselt metaandmetele juurde pääsema ja neid manipuleerima.
Näiteks võiksite kasutada Reflect Metadata
't, et salvestada teavet omaduse andmetüübi, valideerimisreeglite või autoriseerimisnõuete kohta. Dekoraatorid saavad seda metaandmeid seejärel kasutada selliste toimingute tegemiseks nagu sisendi valideerimine, andmete serialiseerimine või turvapoliitikate jõustamine.
Dekoraatorite tüübid koos näidetega
1. Klassidekoraatorid
Klassidekoraatorid rakendatakse klassi konstruktorile. Neid saab kasutada klassi definitsiooni muutmiseks, uute omaduste või meetodite lisamiseks või isegi kogu klassi asendamiseks teisega.
Näide: Singletoni mustri rakendamine
Singletoni muster tagab, et klassist luuakse alati ainult ĂĽks isend. Siin on, kuidas saate seda rakendada klassidekoraatori abil:
function Singleton(target) {
let instance = null;
return function (...args) {
if (!instance) {
instance = new target(...args);
}
return instance;
};
}
@Singleton
class DatabaseConnection {
constructor(connectionString) {
this.connectionString = connectionString;
console.log(`Connecting to ${connectionString}`);
}
query(sql) {
console.log(`Executing query: ${sql}`);
}
}
const db1 = new DatabaseConnection('mongodb://localhost:27017');
const db2 = new DatabaseConnection('mongodb://localhost:27017');
console.log(db1 === db2); // Väljund: true
Selles näites mähitseb Singleton
dekoraator DatabaseConnection
klassi. See tagab, et klassist luuakse alati ainult ĂĽks isend, olenemata sellest, mitu korda konstruktorit kutsutakse.
2. Meetodidekoraatorid
Meetodidekoraatorid rakendatakse klassi sees olevatele meetoditele. Neid saab kasutada meetodi käitumise muutmiseks, logimise lisamiseks, vahemälu rakendamiseks või juurdepääsukontrolli jõustamiseks.
Näide: Meetodikutsete logimineSee dekoraator logib meetodi nime ja selle argumendid iga kord, kui meetodit kutsutakse.
function logMethod(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
console.log(`Calling method: ${propertyKey} with arguments: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
console.log(`Method ${propertyKey} returned: ${result}`);
return result;
};
return descriptor;
}
class Calculator {
@logMethod
add(x, y) {
return x + y;
}
@logMethod
subtract(x, y) {
return x - y;
}
}
const calc = new Calculator();
calc.add(5, 3); // Logib: Calling method: add with arguments: [5,3]
// Method add returned: 8
calc.subtract(10, 4); // Logib: Calling method: subtract with arguments: [10,4]
// Method subtract returned: 6
Siin mähitseb logMethod
dekoraator algse meetodi. Enne algse meetodi käivitamist logib see meetodi nime ja selle argumendid. Pärast käivitamist logib see tagastatava väärtuse.
3. Omaduste dekoraatorid
Omaduste dekoraatorid rakendatakse klassi sees olevatele omadustele. Neid saab kasutada omaduse käitumise muutmiseks, valideerimise rakendamiseks või metaandmete lisamiseks.
Näide: Omaduste väärtuste valideerimine
function validate(target, propertyKey) {
let value;
const getter = function () {
return value;
};
const setter = function (newValue) {
if (typeof newValue !== 'string' || newValue.length < 3) {
throw new Error(`Property ${propertyKey} must be a string with at least 3 characters.`);
}
value = newValue;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
}
class User {
@validate
name;
}
const user = new User();
try {
user.name = 'Jo'; // Väljastab vea
} catch (error) {
console.error(error.message);
}
user.name = 'John Doe'; // Töötab korrektselt
console.log(user.name);
Selles näites püüab validate
dekoraator kinni juurdepääsu name
omadusele. Kui uus väärtus määratakse, kontrollib see, kas väärtus on sõne ja kas selle pikkus on vähemalt 3 tähemärki. Kui ei, siis viskab see vea.
4. Pöördusmeetodite (accessor) dekoraatorid
Pöördusmeetodite dekoraatorid rakendatakse get- ja set-meetoditele. Need sarnanevad meetodidekoraatoritega, kuid on suunatud spetsiaalselt pöördusmeetoditele (getters ja setters).
Näide: Getter'i tulemuste puhverdamine
function cached(target, propertyKey, descriptor) {
const originalGetter = descriptor.get;
let cacheValue;
let cacheSet = false;
descriptor.get = function () {
if (cacheSet) {
console.log(`Returning cached value for ${propertyKey}`);
return cacheValue;
} else {
console.log(`Calculating and caching value for ${propertyKey}`);
cacheValue = originalGetter.call(this);
cacheSet = true;
return cacheValue;
}
};
return descriptor;
}
class Circle {
constructor(radius) {
this.radius = radius;
}
@cached
get area() {
console.log('Calculating area...');
return Math.PI * this.radius * this.radius;
}
}
const circle = new Circle(5);
console.log(circle.area); // Arvutab ja puhverdab pindala
console.log(circle.area); // Tagastab puhverdatud pindala
cached
dekoraator mähitseb area
omaduse getteri. Esimest korda, kui area
'le juurde pääsetakse, käivitatakse getter ja tulemus puhverdatakse. Järgmised juurdepääsud tagastavad puhverdatud väärtuse ilma uuesti arvutamata.
5. Parameetrite dekoraatorid
Parameetrite dekoraatorid rakendatakse meetodite parameetritele. Neid saab kasutada parameetrite kohta metaandmete lisamiseks, sisendi valideerimiseks või parameetrite väärtuste muutmiseks.
Näide: E-posti parameetri valideerimine
const requiredMetadataKey = Symbol("required");
function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
let existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata(requiredMetadataKey, existingRequiredParameters, target, propertyKey);
}
function validateEmail(email: string) {
const emailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g;
return emailRegex.test(email);
}
function validate(target: any, propertyName: string, descriptor: TypedPropertyDescriptor) {
let method = descriptor.value!;
descriptor.value = function () {
let requiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyName);
if (requiredParameters) {
for (let parameterIndex of requiredParameters) {
if(arguments.length <= parameterIndex){
throw new Error("Missing required argument.");
}
const email = arguments[parameterIndex];
if (!validateEmail(email)) {
throw new Error(`Invalid email format for argument #${parameterIndex + 1}.`);
}
}
}
return method.apply(this, arguments);
}
}
class EmailService {
@validate
sendEmail(@required to: string, subject: string, body: string) {
console.log(`Sending email to ${to} with subject: ${subject}`);
}
}
const emailService = new EmailService();
try {
emailService.sendEmail('invalid-email', 'Hello', 'This is a test email.'); // Väljastab vea
} catch (error) {
console.error(error.message);
}
emailService.sendEmail('valid@email.com', 'Hello', 'This is a test email.'); // Töötab korrektselt
Selles näites märgib @required
dekoraator to
parameetri kohustuslikuks ja näitab, et see peab olema kehtivas e-posti vormingus. validate
dekoraator kasutab seejärel Reflect Metadata
't selle teabe hankimiseks ja parameetri valideerimiseks käitusajal.
Dekoraatorite kasutamise eelised
- Parem koodi loetavus ja hooldatavus: Dekoraatorid pakuvad deklaratiivset süntaksit, mis muudab koodi lihtsamini mõistetavaks ja hooldatavaks.
- Suurem koodi taaskasutatavus: Dekoraatoreid saab taaskasutada mitmetes klassides ja meetodites, vähendades koodi dubleerimist.
- Ülesannete eraldamine: Dekoraatorid edendavad ülesannete eraldamist, võimaldades teil lisada täiendavaid käitumisviise ilma põhilist loogikat muutmata.
- Suurem paindlikkus: Dekoraatorid pakuvad paindlikku viisi koodielementide käitumise muutmiseks käitusajal.
- AOP (aspekt-orienteeritud programmeerimine): Dekoraatorid võimaldavad AOP põhimõtteid, mis võimaldavad teil modulariseerida läbivaid aspekte.
Dekoraatorite kasutusjuhud
Dekoraatoreid saab kasutada mitmesugustes stsenaariumides, sealhulgas:
- Logimine: Meetodikutsete, jõudlusnäitajate või veateadete logimine.
- Valideerimine: Sisendparameetrite või omaduste väärtuste valideerimine.
- Vahemälu: Meetodite tulemuste puhverdamine jõudluse parandamiseks.
- Autoriseerimine: Juurdepääsukontrolli poliitikate jõustamine.
- Sõltuvuste süstimine (Dependency Injection): Objektidevaheliste sõltuvuste haldamine.
- Serialiseerimine/deserialiseerimine: Objektide teisendamine erinevatesse vormingutesse ja tagasi.
- Andmete sidumine (Data Binding): Kasutajaliidese elementide automaatne värskendamine andmete muutumisel.
- Oleku haldamine (State Management): Oleku haldamise mustrite rakendamine rakendustes nagu React või Angular.
- API versioonimine: Meetodite või klasside märgistamine konkreetse API versiooni alla kuuluvaks.
- Funktsioonilipud (Feature Flags): Funktsioonide lubamine või keelamine konfiguratsiooniseadete alusel.
Dekoraatoritehased
Dekoraatoritehas on funktsioon, mis tagastab dekoraatori. See võimaldab teil kohandada dekoraatori käitumist, edastades tehasefunktsioonile argumente.
Näide: Parameetritega logija
function logMethodWithPrefix(prefix: string) {
return function (target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
console.log(`${prefix}: Calling method: ${propertyKey} with arguments: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
console.log(`${prefix}: Method ${propertyKey} returned: ${result}`);
return result;
};
return descriptor;
};
}
class Calculator {
@logMethodWithPrefix('[CALCULATION]')
add(x, y) {
return x + y;
}
@logMethodWithPrefix('[CALCULATION]')
subtract(x, y) {
return x - y;
}
}
const calc = new Calculator();
calc.add(5, 3); // Logib: [CALCULATION]: Calling method: add with arguments: [5,3]
// [CALCULATION]: Method add returned: 8
calc.subtract(10, 4); // Logib: [CALCULATION]: Calling method: subtract with arguments: [10,4]
// [CALCULATION]: Method subtract returned: 6
Funktsioon logMethodWithPrefix
on dekoraatoritehas. See võtab argumendiks prefix
ja tagastab dekoraatorifunktsiooni. Dekoraatorifunktsioon logib seejärel meetodikutseid määratud eesliitega.
Reaalse maailma näited ja juhtumiuuringud
Mõelgem ülemaailmsele e-kaubanduse platvormile. Nad võivad kasutada dekoraatoreid järgmistel eesmärkidel:
- Rahvusvahelistamine (i18n): Dekoraatorid võivad teksti automaatselt tõlkida vastavalt kasutaja lokaadile. Dekoraator
@translate
võiks märgistada omadused või meetodid, mis vajavad tõlkimist. Dekoraator hangiks seejärel sobiva tõlke ressursifailist vastavalt kasutaja valitud keelele. - Valuuta konverteerimine: Hindade kuvamisel võiks dekoraator
@currency
hinna automaatselt teisendada kasutaja kohalikku valuutasse. See dekoraator peaks pääsema juurde välisele valuutakursside API-le ja salvestama konversioonimäärad. - Maksude arvutamine: Maksureeglid varieeruvad riigiti ja piirkonniti märkimisväärselt. Dekoraatoreid saaks kasutada õige maksumäära rakendamiseks vastavalt kasutaja asukohale ja ostetavale tootele. Dekoraator
@tax
võiks kasutada geolokatsiooni teavet sobiva maksumäära määramiseks. - Pettuste tuvastamine: Dekoraator
@fraudCheck
tundlike toimingute (nagu ostu vormistamine) puhul võiks käivitada pettuste tuvastamise algoritme.
Teine näide on ülemaailmne logistikaettevõte:
- Geolokatsiooni jälgimine: Dekoraatorid saavad täiustada meetodeid, mis tegelevad asukohaandmetega, logides GPS-näitude täpsust või valideerides asukohavorminguid (laius-/pikkuskraad) erinevate piirkondade jaoks. Dekoraator
@validateLocation
saab tagada, et koordinaadid vastavad enne töötlemist konkreetsele standardile (nt ISO 6709). - Ajavööndite käsitlemine: Tarneid planeerides saavad dekoraatorid ajad automaatselt teisendada kasutaja kohalikku ajavööndisse. Dekoraator
@timeZone
kasutaks teisenduse tegemiseks ajavööndi andmebaasi, tagades, et tarnegraafikud on täpsed olenemata kasutaja asukohast. - Marsruudi optimeerimine: Dekoraatoreid saaks kasutada tarnetaotluste lähte- ja sihtkoha aadresside analüüsimiseks. Dekoraator
@routeOptimize
võiks kutsuda välise marsruudi optimeerimise API, et leida kõige tõhusam marsruut, võttes arvesse selliseid tegureid nagu liiklusolud ja teesulud erinevates riikides.
Dekoraatorid ja TypeScript
TypeScriptil on suurepärane tugi dekoraatoritele. Dekoraatorite kasutamiseks TypeScriptis peate lubama kompilaatori valiku experimentalDecorators
oma tsconfig.json
failis:
{
"compilerOptions": {
"target": "es6",
"experimentalDecorators": true,
// ... muud valikud
}
}
TypeScript pakub dekoraatorite jaoks tüübiinfot, mis muudab nende kirjutamise ja hooldamise lihtsamaks. TypeScript jõustab ka tüübikindlust dekoraatorite kasutamisel, aidates vältida vigu käitusajal. Selle blogipostituse koodinäited on parema tüübikindluse ja loetavuse huvides kirjutatud peamiselt TypeScriptis.
Dekoraatorite tulevik
Dekoraatorid on JavaScriptis suhteliselt uus funktsioon, kuid neil on potentsiaal oluliselt mõjutada seda, kuidas me koodi kirjutame ja struktureerime. Kuna JavaScripti ökosüsteem areneb edasi, võime oodata rohkem teeke ja raamistikke, mis kasutavad dekoraatoreid uute ja uuenduslike funktsioonide pakkumiseks. Dekoraatorite standardimine ES2022-s tagab nende pikaajalise elujõulisuse ja laialdase kasutuselevõtu.
Väljakutsed ja kaalutlused
- Keerukus: Dekoraatorite liigne kasutamine võib viia keeruka koodini, mida on raske mõista. Oluline on neid kasutada läbimõeldult ja dokumenteerida põhjalikult.
- Jõudlus: Dekoraatorid võivad lisada üldkulusid, eriti kui nad teostavad keerulisi toiminguid käitusajal. On oluline arvestada dekoraatorite kasutamise jõudlusmõjudega.
- Silumine (Debugging): Dekoraatoreid kasutava koodi silumine võib olla keeruline, kuna täitmise voog võib olla vähem sirgjooneline. Head logimistavad ja silumistööriistad on hädavajalikud.
- Õppimiskõver: Arendajad, kes pole dekoraatoritega tuttavad, peavad võib-olla investeerima aega nende toimimise õppimisse.
Parimad praktikad dekoraatorite kasutamisel
- Kasutage dekoraatoreid säästlikult: Kasutage dekoraatoreid ainult siis, kui need pakuvad selget kasu koodi loetavuse, taaskasutatavuse või hooldatavuse osas.
- Dokumenteerige oma dekoraatorid: Dokumenteerige selgelt iga dekoraatori eesmärk ja käitumine.
- Hoidke dekoraatorid lihtsad: Vältige keerulist loogikat dekoraatorite sees. Vajadusel delegeerige keerulised toimingud eraldi funktsioonidele.
- Testige oma dekoraatoreid: Testige oma dekoraatoreid põhjalikult, et tagada nende korrektne toimimine.
- Järgige nimekonventsioone: Kasutage dekoraatorite jaoks järjepidevat nimekonventsiooni (nt
@LogMethod
,@ValidateInput
). - Arvestage jõudlusega: Olge teadlik dekoraatorite kasutamise jõudlusmõjudest, eriti jõudluskriitilises koodis.
Kokkuvõte
JavaScript'i dekoraatorid pakuvad võimsat ja paindlikku viisi koodi taaskasutatavuse suurendamiseks, hooldatavuse parandamiseks ja läbivate aspektide rakendamiseks. Mõistes dekoraatorite põhikontseptsioone ja Reflect Metadata
API-d, saate neid kasutada väljendusrikkamate ja modulaarsemate rakenduste loomiseks. Kuigi on kaalutluskohti, kaaluvad dekoraatorite kasutamise eelised sageli üles puudused, eriti suurtes ja keerukates projektides. Kuna JavaScripti ökosüsteem areneb, mängivad dekoraatorid tõenäoliselt üha olulisemat rolli selles, kuidas me koodi kirjutame ja struktureerime. Katsetage esitatud näidetega ja uurige, kuidas dekoraatorid saavad teie projektides konkreetseid probleeme lahendada. Selle võimsa funktsiooni omaksvõtmine võib viia elegantsemate, hooldatavamate ja robustsemate JavaScripti rakendusteni erinevates rahvusvahelistes kontekstides.