JavaScript Dekoratorlarini o'rganing: metama'lumotlar qo'shing, klasslar/metodlarni o'zgartiring va kodingiz funksionalligini toza, deklarativ usulda yaxshilang.
JavaScript Dekoratorlari: Metama'lumotlar va Transformatsiya
Python va Java kabi tillardan ilhomlangan JavaScript Dekoratorlari metama'lumotlar qo'shish va klasslar, metodlar, xususiyatlar hamda parametrlarni o'zgartirishning kuchli va ifodali usulini taqdim etadi. Ular kod funksionalligini yaxshilash va mas'uliyatlarni ajratish uchun toza, deklarativ sintaksisni taklif qiladi. JavaScript ekotizimiga nisbatan yangi qo'shimcha bo'lishiga qaramay, dekoratorlar, ayniqsa Angular kabi freymvorklar va bog'liqliklarni kiritish (dependency injection) hamda boshqa ilg'or xususiyatlar uchun metama'lumotlardan foydalanadigan kutubxonalar orasida mashhurlikka erishmoqda. Ushbu maqola JavaScript dekoratorlarining asoslari, ularning qo'llanilishi va yanada barqaror hamda kengaytiriladigan kod bazalarini yaratishdagi salohiyatini o'rganadi.
JavaScript Dekoratorlari nima?
Aslida, dekoratorlar bu klasslar, metodlar, aktsessorlar, xususiyatlar yoki parametrlarga biriktirilishi mumkin bo'lgan maxsus turdagi deklaratsiyalardir. Ular @expression
sintaksisidan foydalanadi, bu yerda expression
ish vaqtida dekoratsiya qilingan deklaratsiya haqidagi ma'lumotlar bilan chaqiriladigan funksiyaga baholanishi kerak. Dekoratorlar asosan dekoratsiya qilingan elementning xatti-harakatini o'zgartiradigan yoki kengaytiradigan funksiyalar sifatida ishlaydi.
Dekoratorlarni mavjud kodni to'g'ridan-to'g'ri o'zgartirmasdan uni o'rash yoki kengaytirish usuli deb o'ylang. Dasturiy ta'minot dizaynidagi Dekorator naqshi (Decorator pattern) deb nomlanuvchi bu tamoyil obyektga dinamik ravishda funksionallik qo'shish imkonini beradi.
Dekoratorlarni yoqish
Dekoratorlar ECMAScript standartining bir qismi bo'lsa-da, ular ko'pchilik JavaScript muhitlarida sukut bo'yicha yoqilmagan. Ulardan foydalanish uchun odatda qurish vositalaringizni (build tools) sozlash kerak bo'ladi. Quyida ba'zi umumiy muhitlarda dekoratorlarni qanday yoqish ko'rsatilgan:
- TypeScript: Dekoratorlar TypeScript'da tabiiy ravishda qo'llab-quvvatlanadi.
tsconfig.json
faylingizdaexperimentalDecorators
kompilyator opsiyasitrue
ga o'rnatilganligiga ishonch hosil qiling:
{
"compilerOptions": {
"target": "esnext",
"experimentalDecorators": true,
"emitDecoratorMetadata": true, // Ixtiyoriy, lekin ko'pincha foydali
"module": "commonjs", // Yoki "es6" yoki "esnext" kabi boshqa modul tizimi
"moduleResolution": "node"
}
}
- Babel: Agar siz Babel'dan foydalanayotgan bo'lsangiz,
@babel/plugin-proposal-decorators
plaginini o'rnatishingiz va sozlashingiz kerak bo'ladi:
npm install --save-dev @babel/plugin-proposal-decorators
Keyin, plaginni Babel konfiguratsiyangizga qo'shing (masalan, .babelrc
yoki babel.config.js
):
{
"plugins": [["@babel/plugin-proposal-decorators", { "version": "2023-05" }]]
}
version
opsiyasi muhim va siz maqsad qilgan dekoratorlar taklifi versiyasiga mos kelishi kerak. Eng so'nggi tavsiya etilgan versiya uchun Babel plaginining hujjatlariga murojaat qiling.
Dekorator turlari
Har biri maxsus elementlar uchun mo'ljallangan bir nechta dekorator turlari mavjud:
- Klass Dekoratorlari: Klasslarga qo'llaniladi.
- Metod Dekoratorlari: Klass ichidagi metodlarga qo'llaniladi.
- Aktsessor Dekoratorlari: Getter yoki setter aktsessorlariga qo'llaniladi.
- Xususiyat Dekoratorlari: Klassning xususiyatlariga qo'llaniladi.
- Parametr Dekoratorlari: Metod yoki konstruktor parametrlariga qo'llaniladi.
Klass Dekoratorlari
Klass dekoratorlari klass konstruktoriga qo'llaniladi va klass ta'rifini kuzatish, o'zgartirish yoki almashtirish uchun ishlatilishi mumkin. Ular yagona argument sifatida klass konstruktorini qabul qiladi.
Misol:
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
// Muhrlangan klassga yoki uning prototipiga xususiyatlar qo'shishga urinish muvaffaqiyatsiz tugaydi
Ushbu misolda @sealed
dekoratori Greeter
klassiga va uning prototipiga keyingi o'zgartirishlar kiritishni oldini oladi. Bu o'zgarmaslikni ta'minlash yoki tasodifiy o'zgarishlarning oldini olish uchun foydali bo'lishi mumkin.
Metod Dekoratorlari
Metod dekoratorlari klass ichidagi metodlarga qo'llaniladi. Ular uchta argument qabul qiladi:
target
: Klass prototipi (obyekt metodlari uchun) yoki klass konstruktori (statik metodlar uchun).propertyKey
: Dekoratsiya qilinayotgan metodning nomi.descriptor
: Metod uchun xususiyat deskriptori.
Misol:
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with arguments: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
console.log(`Method ${propertyKey} returned: ${result}`);
return result;
};
return descriptor;
}
class Calculator {
@log
add(x: number, y: number) {
return x + y;
}
}
const calculator = new Calculator();
calculator.add(2, 3); // Natija: Calling add with arguments: [2,3]
// Method add returned: 5
@log
dekoratori add
metodining argumentlari va qaytariladigan qiymatini log qiladi. Bu metod dekoratorlarini loglash, profil yaratish yoki boshqa kesishuvchi masalalar (cross-cutting concerns) uchun qanday ishlatilishining oddiy misolidir.
Aktsessor Dekoratorlari
Aktsessor dekoratorlari metod dekoratorlariga o'xshaydi, lekin getter yoki setter aktsessorlariga qo'llaniladi. Ular ham xuddi shu uchta argumentni qabul qiladi: target
, propertyKey
va descriptor
.
Misol:
function configurable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.configurable = value;
};
}
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() {
return this._x;
}
set x(value: number) {
this._x = value;
}
}
const point = new Point(1, 2);
// Object.defineProperty(point, 'x', { configurable: true }); // Xatolik yuzaga keladi, chunki 'x' sozlanmaydigan
@configurable(false)
dekoratori x
getterini qayta sozlanishdan saqlaydi va uni sozlanmaydigan qilib qo'yadi.
Xususiyat Dekoratorlari
Xususiyat dekoratorlari klassning xususiyatlariga qo'llaniladi. Ular ikkita argument qabul qiladi:
target
: Klass prototipi (obyekt xususiyatlari uchun) yoki klass konstruktori (statik xususiyatlar uchun).propertyKey
: Dekoratsiya qilinayotgan xususiyatning nomi.
Misol:
function readonly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false,
});
}
class Person {
@readonly
name: string;
constructor(name: string) {
this.name = name;
}
}
const person = new Person("Alice");
// person.name = "Bob"; // Bu qat'iy rejimda xatolikka olib keladi, chunki 'name' faqat o'qish uchun mo'ljallangan
@readonly
dekoratori name
xususiyatini faqat o'qish uchun mo'ljallangan (read-only) qilib qo'yadi va uni ishga tushirilgandan keyin o'zgartirishni oldini oladi.
Parametr Dekoratorlari
Parametr dekoratorlari metod yoki konstruktor parametrlariga qo'llaniladi. Ular uchta argument qabul qiladi:
target
: Klass prototipi (obyekt metodlari uchun) yoki klass konstruktori (statik metodlar yoki konstruktorlar uchun).propertyKey
: Metod yoki konstruktorning nomi.parameterIndex
: Parametrlar ro'yxatidagi parametrning indeksi.
Parametr dekoratorlari ko'pincha funksiya parametrlari haqida metama'lumotlarni saqlash uchun refleksiya bilan birga ishlatiladi. Ushbu metama'lumotlar keyinchalik ish vaqtida bog'liqliklarni kiritish (dependency injection) yoki boshqa maqsadlar uchun ishlatilishi mumkin. Bu to'g'ri ishlashi uchun siz tsconfig.json
faylingizda emitDecoratorMetadata
kompilyator opsiyasini yoqishingiz kerak.
Misol (reflect-metadata
yordamida):
import 'reflect-metadata';
function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
let existingRequiredParameters: number[] = Reflect.getOwnMetadata("required", target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata("required", existingRequiredParameters, target, propertyKey);
}
function validate(target: any, propertyName: string, descriptor: TypedPropertyDescriptor) {
let method = descriptor.value!;
descriptor.value = function (...args: any[]) {
let requiredParameters: number[] = Reflect.getOwnMetadata("required", target, propertyName);
if (requiredParameters) {
for (let parameterIndex of requiredParameters) {
if (args[parameterIndex] === null || args[parameterIndex] === undefined) {
throw new Error(`Missing required argument at index ${parameterIndex}`);
}
}
}
return method.apply(this, args);
};
}
class User {
name: string;
age: number;
constructor(@required name: string, public surname: string, @required age: number) {
this.name = name;
this.age = age;
}
@validate
greet(prefix: string, @required salutation: string): string {
return `${prefix} ${salutation} ${this.name}`;
}
}
// Usage
try {
const user1 = new User("John", "Doe", 30);
console.log(user1.greet("Mr.", "Hello"));
const user2 = new User(undefined as any, "Doe", null as any);
} catch (error) {
console.error(error.message);
}
try {
const user = new User("John", "Doe", 30);
console.log(user.greet("Mr.", undefined as any));
} catch (error) {
console.error(error.message);
}
Ushbu misolda @required
dekoratori parametrlarni majburiy deb belgilaydi. Keyin @validate
dekoratori metodni chaqirishdan oldin majburiy parametrlar mavjudligini tekshirish uchun refleksiya (reflect-metadata
orqali) dan foydalanadi. Bu misol asosiy qo'llanilishni ko'rsatadi va ishlab chiqarish stsenariysida mustahkam parametr tekshiruvini yaratish tavsiya etiladi.
reflect-metadata
'ni o'rnatish uchun:
npm install reflect-metadata --save
Metama'lumotlar uchun Dekoratorlardan foydalanish
Dekoratorlarning asosiy qo'llanilishlaridan biri klasslarga va ularning a'zolariga metama'lumotlar biriktirishdir. Ushbu metama'lumotlar ish vaqtida turli maqsadlar uchun ishlatilishi mumkin, masalan, bog'liqliklarni kiritish, serializatsiya va validatsiya. reflect-metadata
kutubxonasi metama'lumotlarni saqlash va olishning standart usulini taqdim etadi.
Misol:
import 'reflect-metadata';
const TYPE_KEY = "design:type";
const PARAMTYPES_KEY = "design:paramtypes";
const RETURNTYPE_KEY = "design:returntype";
function Type(type: any) {
return Reflect.metadata(TYPE_KEY, type);
}
function LogType(target: any, propertyKey: string) {
const t = Reflect.getMetadata(TYPE_KEY, target, propertyKey);
console.log(`${target.constructor.name}.${propertyKey} type: ${t.name}`);
}
class Demo {
@LogType
public name: string;
constructor(name: string){
this.name = name;
}
}
Dekorator Fabrikalari
Dekorator fabrikalari dekoratorni qaytaradigan funksiyalardir. Ular dekoratorga argumentlar uzatish imkonini beradi, bu esa uni yanada moslashuvchan va qayta ishlatiladigan qiladi.
Misol:
function deprecated(deprecationReason: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.warn(`Method ${propertyKey} is deprecated: ${deprecationReason}`);
return originalMethod.apply(this, args);
};
return descriptor;
};
}
class LegacyComponent {
@deprecated("Use the newMethod instead.")
oldMethod() {
console.log("Old method called");
}
newMethod() {
console.log("New method called");
}
}
const component = new LegacyComponent();
component.oldMethod(); // Natija: Method oldMethod is deprecated: Use the newMethod instead.
// Old method called
@deprecated
dekorator fabrikasi eskirganlik xabarini argument sifatida qabul qiladi va dekoratsiya qilingan metod chaqirilganda ogohlantirishni log qiladi. Bu sizga metodlarni eskirgan deb belgilash va dasturchilarga yangi alternativlarga o'tish bo'yicha yo'l-yo'riq ko'rsatish imkonini beradi.
Haqiqiy hayotdagi qo'llash holatlari
Dekoratorlar zamonaviy JavaScript dasturlashida keng ko'lamli qo'llanilishga ega:
- Bog'liqliklarni kiritish: Angular kabi freymvorklar bog'liqliklarni kiritish uchun dekoratorlarga ko'p tayanadi.
- Marshrutlash: Veb-ilovalarida dekoratorlar kontrollerlar va metodlar uchun marshrutlarni aniqlash uchun ishlatilishi mumkin.
- Validatsiya: Dekoratorlar kiruvchi ma'lumotlarni tekshirish va ularning ma'lum mezonlarga javob berishini ta'minlash uchun ishlatilishi mumkin.
- Avtorizatsiya: Dekoratorlar xavfsizlik siyosatlarini amalga oshirish va ma'lum metodlar yoki resurslarga kirishni cheklash uchun ishlatilishi mumkin.
- Loglash va Profil yaratish: Yuqoridagi misollarda ko'rsatilganidek, dekoratorlar kod bajarilishini loglash va profil yaratish uchun ishlatilishi mumkin.
- Holatni Boshqarish: Dekoratorlar holat o'zgarganda komponentlarni avtomatik ravishda yangilash uchun holatni boshqarish kutubxonalari bilan integratsiya qilinishi mumkin.
Dekoratorlardan foydalanishning afzalliklari
- Kodning o'qilishi osonlashishi: Dekoratorlar funksionallik qo'shish uchun deklarativ sintaksisni taqdim etadi, bu esa kodni tushunish va saqlashni osonlashtiradi.
- Mas'uliyatlarni ajratish: Dekoratorlar kesishuvchi masalalarni (masalan, loglash, validatsiya, avtorizatsiya) asosiy biznes mantig'idan ajratish imkonini beradi.
- Qayta ishlatiluvchanlik: Dekoratorlar bir nechta klasslar va metodlar bo'ylab qayta ishlatilishi mumkin, bu esa kod takrorlanishini kamaytiradi.
- Kengaytiriluvchanlik: Dekoratorlar mavjud kodning funksionalligini to'g'ridan-to'g'ri o'zgartirmasdan kengaytirishni osonlashtiradi.
Qiyinchiliklar va e'tiborga olinadigan jihatlar
- O'rganish egri chizig'i: Dekoratorlar nisbatan yangi xususiyat bo'lib, ulardan samarali foydalanishni o'rganish uchun biroz vaqt talab qilinishi mumkin.
- Moslik: Maqsadli muhitingiz dekoratorlarni qo'llab-quvvatlashiga va qurish vositalaringizni to'g'ri sozlanganligiga ishonch hosil qiling.
- Nosozliklarni tuzatish: Dekoratorlardan foydalanadigan kodni tuzatish oddiy kodni tuzatishdan ko'ra qiyinroq bo'lishi mumkin, ayniqsa dekoratorlar murakkab bo'lsa.
- Haddan tashqari foydalanish: Dekoratorlardan haddan tashqari foydalanishdan saqlaning, chunki bu kodingizni tushunish va saqlashni qiyinlashtirishi mumkin. Ularni strategik ravishda ma'lum maqsadlar uchun ishlating.
- Ish vaqtidagi qo'shimcha yuk: Dekoratorlar, ayniqsa murakkab operatsiyalarni bajarsa, ish vaqtida biroz qo'shimcha yuk keltirib chiqarishi mumkin. Yuqori unumdorlik talab qilinadigan ilovalarda dekoratorlardan foydalanganda unumdorlik oqibatlarini hisobga oling.
Xulosa
JavaScript Dekoratorlari kod funksionalligini yaxshilash va mas'uliyatlarni ajratish uchun kuchli vositadir. Metama'lumotlar qo'shish va klasslar, metodlar, xususiyatlar hamda parametrlarni o'zgartirish uchun toza, deklarativ sintaksisni taqdim etish orqali dekoratorlar sizga yanada barqaror, qayta ishlatiladigan va kengaytiriladigan kod bazalarini yaratishga yordam beradi. Ularning o'rganish egri chizig'i va ba'zi potentsial qiyinchiliklari bo'lsa-da, to'g'ri kontekstda dekoratorlardan foydalanishning afzalliklari sezilarli bo'lishi mumkin. JavaScript ekotizimi rivojlanishda davom etar ekan, dekoratorlar zamonaviy JavaScript dasturlashining tobora muhimroq qismiga aylanishi mumkin.
Dekoratorlar mavjud kodingizni qanday soddalashtirishi yoki sizga yanada ifodali va barqaror ilovalar yozish imkonini berishi mumkinligini o'rganib chiqing. Puxta rejalashtirish va ularning imkoniyatlarini yaxshi tushunish bilan siz yanada mustahkam va kengaytiriladigan JavaScript yechimlarini yaratish uchun dekoratorlardan foydalanishingiz mumkin.