IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur39/65-dars22 daqiqa
Mundarija (32)

JavaScript klassida getter, setter va static: nusxaniki va klassning o'ziniki

Qisqacha: Klassdagi get narx() va set narx(q) — xususiyat o'qilganda yoki yozilganda ishlaydigan funksiyalar: qiymatni hisoblaydi yoki tekshiradi, tashqaridan esa oddiy osh.narx bo'lib ko'rinadi. static bilan yozilgan a'zo esa nusxaga emas, klassning o'ziga tegishli: Taom.dan(malumot), Array.from(...). Nusxaning ma'lumoti — oddiy a'zoda, hamma nusxa uchun umumiy narsa — static da.

Bu darsda

  • Klass ichida getter va setter yoza olasiz: hisoblanadigan xususiyat, tekshiruvli yozish va faqat o'qiladigan xususiyat.
  • static maydon va metod nima ekanini, ular qayerda yashashini tushuntira olasiz.
  • static metod ichidagi this ni va uning tuzog'ini bilasiz.
  • static {} bloki va factory metod (Taom.dan(...)) ni yoza olasiz.
  • vazifalar dagi Vazifa klassiga static o'zgarmas va factory metod qo'shasiz.

Oldin bilishingiz kerak: Getter va setter, class sintaksisi, Class maydonlari, Private # maydon va metodlar.

1. Nega bu kerak?

Private # maydon darsida narxni #narx ga yashirdik. Endi uni tashqaridan hech kim buzolmaydi. Lekin ikki savol ochiq qoldi.

Birinchisi: narxni o'qish va yangilash baribir kerak. Rustam aka oshning narxini 35 000 dan 38 000 ga ko'tarsa-chi? osh.narxniOzgartir(38000) kabi metod yozish mumkin. Lekin osh.narx = 38000 ancha tabiiy o'qiladi. Faqat bitta shart bilan: manfiy yoki matnli narx kirib qolmasin.

Ikkinchisi: ba'zi ma'lumot hech bir taomga tegishli emas. "Menyuda nechta taom yaratildi?", "narx qaysi valyutada?" — bu savollar oshga ham, mantiga ham emas, taomlar umumiga tegishli. Har nusxaga nusxa qilib yozish — isrof va chalkashlik.

Hayotdan o'xshatish. «Bahor» dagi har stol — alohida: birida 4 kishi, birida 2 kishi o'tiribdi. Bu — nusxaning ma'lumoti. Eshik oldidagi "Ish vaqti: 7:00–23:00" lavhasi esa bitta, choyxonaning o'ziga tegishli. Uni har stolga yopishtirmaysiz. Klassda ham shunday: stolning o'z ma'lumoti — oddiy a'zo, eshikdagi lavha — static.

Bu dars ikkala savolni yopadi. Avval getter va setter — xususiyatni "boshqariladigan" qilamiz. Keyin static — klassning o'z a'zolari. Oxirida ikkalasini birlashtiradigan naqsh — factory metod.

2. Getter va setter klassda

2.1 Eslab olamiz: obyektdagi getter

Getter va setter darsida obyekt literalida yozgan edik:

js
const savat = {
  narxlar: [35000, 5000],
  get jami() {
    return this.narxlar.reduce((s, n) => s + n, 0);
  },
};

console.log(savat.jami); // 40000

jami — funksiya, lekin qavssiz o'qiladi. Getter (getter) — xususiyat o'qilganda chaqiriladigan funksiya. Setter (setter) — xususiyatga qiymat yozilganda chaqiriladigan funksiya. Klassda sintaksis aynan shunday, faqat vergulsiz — metodlar kabi.

2.2 # maydon + getter + setter

Eng ko'p uchraydigan shakl: qiymat private maydonda, tashqariga getter va setter orqali chiqadi.

js
class Taom {
  #narx;

  constructor(nom, narx) {
    this.nom = nom;
    this.narx = narx; // setter orqali o'tadi
  }

  get narx() {
    return this.#narx;
  }

  set narx(yangi) {
    if (!Number.isInteger(yangi) || yangi <= 0) {
      console.warn(`Noto'g'ri narx: ${yangi}`);
      return;
    }
    this.#narx = yangi;
  }
}

const osh = new Taom("Osh", 35000);
osh.narx = 38000;
console.log(osh.narx);
osh.narx = -5;
osh.narx = "40 000";
console.log(osh.narx);

Konsolda:

text
38000
Noto'g'ri narx: -5
Noto'g'ri narx: 40 000
38000

Qatorma-qator:

  1. this.narx = narx — konstruktor ham setter orqali yozadi. Shuning uchun yaratishda ham tekshiruv ishlaydi: new Taom("Osh", -1) ham ogohlantiradi.
  2. osh.narx = 38000 — oddiy tayinlashga o'xshaydi, lekin aslida set narx(38000) chaqirildi. Tekshiruvdan o'tdi va #narx ga yozildi.
  3. -5 va "40 000" — setter ularni rad etdi. #narx o'zgarmadi, shuning uchun oxirida yana 38000.

Tashqaridan qaraganda osh.narx — oddiy xususiyat. Ichkarida esa uni qo'riqchi qo'riqlayapti. Xuddi bankomat kabi: siz "pul yechish" tugmasini bosasiz, ichkarida esa balans tekshiriladi.

Maslahat: Bu darsda noto'g'ri qiymatni console.warn bilan bildirib, e'tiborsiz qoldiryapmiz. Real kodda setter odatda xato tashlaydi — dastur shu joyda to'xtaydi va xato aniq ko'rinadi. Buni throw va Error obyekti darsida o'rganamiz, hozir bilish shart emas.

2.3 Hisoblanadigan xususiyat

Getter saqlangan qiymatni qaytarishi shart emas — u hisoblab berishi mumkin. Buyurtmaning jami summasi hech qayerda saqlanmaydi: har o'qishda taomlardan yig'iladi (reduce).

js
class Buyurtma {
  #taomlar = [];

  qosh(nom, narx) {
    this.#taomlar.push({ nom, narx });
  }

  get jami() {
    return this.#taomlar.reduce((s, t) => s + t.narx, 0);
  }

  get soni() {
    return this.#taomlar.length;
  }
}

const stol4 = new Buyurtma();
stol4.qosh("Osh", 35000);
stol4.qosh("Ko'k choy", 5000);

console.log(stol4.soni, stol4.jami); // 2 40000

Nega jami ni maydonda saqlab, har qosh da yangilab bormaymiz? Chunki unda ikki joyda bitta haqiqat bo'lardi: taomlar ro'yxati va alohida son. Biri yangilanib, ikkinchisi unutilsa — chek noto'g'ri chiqadi. Getter bilan haqiqat bitta: ro'yxat. Jami undan har safar to'g'ri hisoblanadi.

O'zingiz to'ldiring. Uchinchi taom qo'shilsa — stol4.qosh("Manti", 30000) — stol4.jami [:70000] bo'ladi, stol4.soni esa [:3].

2.4 Faqat o'qiladigan xususiyat

Buyurtma da jami uchun setter yo'q. Klass ichidagi biror metod unga yozib ko'rsa nima bo'ladi?

js
class Buyurtma {
  get jami() {
    return 40000;
  }

  tozala() {
    this.jami = 0;
  }
}

const stol4 = new Buyurtma();
stol4.tozala();
text
TypeError: Cannot set property jami of #<Buyurtma> which has only a getter

Tarjimasi: "Buyurtma nusxasining jami xususiyatiga yozib bo'lmaydi — unda faqat getter bor". #<Buyurtma> — Node nusxani shunday belgilaydi: "Buyurtma klassining biror nusxasi".

Klass tashqarisida esa bu xatoni hamma joyda ham ko'rmaysiz. Strict mode darsini eslang: klass ichidagi kod va modul fayllari (.mjs, type="module") doim strict. U yerda jim xatolar haqiqiy xatoga aylanadi. Saytdagi «Ishga tushir» tugmasi ham strict — u yerda ham TypeError ko'rasiz.

Oddiy, strict bo'lmagan <script> da esa stol4.jami = 0 hech narsa demaydi. Tayinlash shunchaki yo'qoladi va jami avvalgidek 40000 qoladi. Ikkala holatda ham qiymat o'zgarmaydi, farq faqat xato ko'rinishida.

Demak getter + setter yo'qligi = faqat o'qiladigan xususiyat. Tashqi kod buyurtma summasini "qo'lda" o'zgartira olmaydi — faqat taom qo'shib.

Tekshirib ko'ring: Taom dagi set narx ni o'chirib, faqat get narx qoldirsak, new Taom("Osh", 35000) nima bo'ladi?

Javob

Konstruktordagi this.narx = narx ham getter-only xususiyatga yozish bo'lib qoladi. Klass ichidagi kod doim strict, shuning uchun TypeError: Cannot set property narx of #<Taom> which has only a getter — nusxa umuman yaratilmaydi. Konstruktor setter'ga tayansa, setter albatta kerak. Yoki konstruktorda to'g'ridan-to'g'ri this.#narx = narx yozing (lekin unda tekshiruv o'tkazib yuboriladi).

2.5 Getter va setter qayerda yashaydi?

osh ning o'z xususiyatlariga qarang:

js
class Taom {
  #narx = 0;
  constructor(nom) {
    this.nom = nom;
  }
  get narx() {
    return this.#narx;
  }
  set narx(yangi) {
    this.#narx = yangi;
  }
}

const osh = new Taom("Osh");
console.log(Object.keys(osh)); // [ 'nom' ]
console.log(Object.getOwnPropertyNames(Taom.prototype));
// [ 'constructor', 'narx' ]

narx nusxada yo'q — u Taom.prototype da. Getter va setter metodlar kabi prototipga yoziladi, hamma nusxa ularni bitta juftlikdan oladi (Prototip zanjiri). #narx qiymati esa har nusxada alohida, lekin Object.keys uni ko'rmaydi — private maydon oddiy xususiyat emas.

Xususiyat deskriptorlari darsidagi "accessor" deskriptorni eslang. Klassdagi get/set aynan shuni yaratadi:

js
class Taom {
  get narx() {
    return 1;
  }
  set narx(q) {}
}

const d = Object.getOwnPropertyDescriptor(Taom.prototype, "narx");
console.log(typeof d.get, typeof d.set, d.enumerable);
// function function false

enumerable: false — klass a'zolari sanalmaydi. Shuning uchun Object.keys va for...in ularni ko'rsatmaydi. Bu farqqa class ichidan darsida qaytamiz.

2.6 Getter qachon kerak, qachon metod?

Getter tashqaridan xususiyatdek ko'rinadi. Demak o'quvchi undan xususiyatdek xulq kutadi. Uch qoida:

  • Tez bo'lsin. stol4.jami ni o'qigan odam u bir zumda qaytadi deb o'ylaydi. Serverdan ma'lumot olish yoki minglab elementni saralash — metod ishi: hisobotniTuz().
  • Yon ta'sirsiz bo'lsin. Getter hech narsani o'zgartirmasin, ekranga chiqarmasin. osh.narx ni ikki marta o'qish ikki marta bir xil natija bersin.
  • Nomi — ot. Getter qiymat, shuning uchun jami, soni, narx. Metod — ish, nomi fe'l: qosh, hisobla.

Tekshirib ko'ring: get chekniChopEt() — printerga chek yuboradigan getter. Bu yaxshi fikrmi?

Javob

Yo'q. Bu yon ta'sir — printer ishlaydi. buyurtma.chekniChopEt ni DevTools'da shunchaki ko'rish uchun ochsangiz ham chek chiqib ketadi. Nomi ham fe'l — o'zi "men metodman" deyapti. To'g'risi: oddiy metod chekniChopEt(), qavs bilan chaqiriladi.

3. static: klassning o'z a'zolari

3.1 static maydon

Narx qaysi valyutada? Bu savol har taom uchun bir xil javob beradi. Uni klassga yozamiz:

js
class Taom {
  static VALYUTA = "so'm";

  constructor(nom, narx) {
    this.nom = nom;
    this.narx = narx;
  }
}

const osh = new Taom("Osh", 35000);
console.log(Taom.VALYUTA); // so'm
console.log(osh.VALYUTA); // undefined
console.log(`${osh.narx} ${Taom.VALYUTA}`); // 35000 so'm

static a'zo (static member) — nusxaga emas, klassning o'ziga tegishli maydon yoki metod. Unga klass nomi orqali murojaat qilinadi: Taom.VALYUTA. Nusxa orqali — osh.VALYUTA — topilmaydi.

Nega? Funksiya obyekt sifatida darsini eslang: funksiya ham obyekt, unga xususiyat qo'shsa bo'ladi. Klass ham funksiya (typeof Taom — "function"). static VALYUTA — shu funksiya-obyektning xususiyati, xolos. osh esa Taom.prototype dan meros oladi, Taom ning o'zidan emas. Ikki xil zanjir:

flowchart TB
  K["Taom — klass (funksiya)<br/>static: VALYUTA, dan()"]
  P["Taom.prototype<br/>get/set narx, metodlar"]
  O["osh — nusxa<br/>nom, #narx"]
  M["manti — nusxa<br/>nom, #narx"]
  K -- ".prototype" --> P
  O -- "[[Prototype]]" --> P
  M -- "[[Prototype]]" --> P

Diagrammada osh va manti dan chiquvchi strelkalar faqat Taom.prototype ga boradi. Taom ning o'ziga hech qaysi nusxadan yo'l yo'q — shuning uchun osh.VALYUTA topilmadi. static a'zo nusxalarning qidiruv zanjirida yo'q.

Bunday o'zgarmaslar odatda KATTA_HARF bilan yoziladi: static VALYUTA, static MAKS_NARX. Bu "o'zgartirilmaydi" degan kelishuv (let va const darsidagi nomlash uslublari).

3.2 static metod

Ikki taomni narx bo'yicha solishtirish — bu bitta taomning ishi emas. Ikkalasi teng huquqli. Bunday funksiya static metod bo'ladi:

js
class Taom {
  constructor(nom, narx) {
    this.nom = nom;
    this.narx = narx;
  }

  static solishtir(a, b) {
    return a.narx - b.narx;
  }
}

const menyu = [
  new Taom("Osh", 35000),
  new Taom("Lag'mon", 28000),
  new Taom("Manti", 30000),
];

const arzondan = menyu.toSorted(Taom.solishtir);
console.log(arzondan.map((t) => t.nom).join(", "));
// Lag'mon, Manti, Osh

Taom.solishtir — sort va taqqoslash funksiyasi darsidagi (a, b) imzosi. Uni qavssiz, to'g'ridan-to'g'ri toSorted ga berdik (Nusxa bilan o'zgartirish). Bu xavfsiz, chunki ichida this yo'q — faqat ikki parametr.

Qachon metod static bo'ladi? Oddiy savol bilan tekshiring: "Bu ishga bitta aniq nusxa kerakmi?" osh.narxniOshir(10) — ha, aynan osh. Taom.solishtir(a, b) — yo'q, ikki teng nusxa. Taom.dan(malumot) — yo'q, nusxa hali yo'q, uni endi yaratamiz.

3.3 Tanish static metodlar

Siz static metodlarni allaqachon o'nlab marta ishlatgansiz — faqat nomini bilmagansiz:

Chaqiruv Klass Nega static
Array.isArray(x) Array Hali massiv ekani noma'lum — x.isArray() deb bo'lmaydi
Array.from("osh") Array Yangi massiv yaratadi (Array.from)
Object.keys(obj) Object Har qanday obyekt uchun
Number.isInteger(n) Number Har qanday qiymat uchun

Endi Object.keys(osh) nega osh.keys() emasligi tushunarli: keys — Object klassining static metodi. osh da bunday metod yo'q va bo'lishi ham kerak emas — aks holda keys nomli maydoni bor obyekt metodni yashirib qo'yardi.

Tekshirib ko'ring: [1, 2].isArray() nima beradi?

Javob

TypeError: [1,2].isArray is not a function. isArray — Array ning static metodi, u Array.prototype da yo'q. Massiv nusxasining zanjirida esa faqat Array.prototype bor. To'g'risi: Array.isArray([1, 2]) — true.

3.4 static ichida this va private static

Nechta taom yaratilganini sanaymiz. Hisoblagich bitta — klassda:

js
class Taom {
  static #soni = 0;

  constructor(nom) {
    this.nom = nom;
    Taom.#soni++;
  }

  static get soni() {
    return this.#soni;
  }
}

new Taom("Osh");
new Taom("Manti");
console.log(Taom.soni); // 2

Uch yangi narsa:

  1. static #soni — private static maydon. Klass tashqarisidan Taom.#soni ga ham kirib bo'lmaydi; uni faqat Taom.soni getteri orqali o'qish mumkin. Yozish esa — faqat konstruktorda. Hisoblagichni tashqaridan "aldab" bo'lmaydi.
  2. static get soni() — getter ham static bo'la oladi. Taom.soni qavssiz o'qiladi.
  3. static metod ichida this — klassning o'zi. this: to'rt qoida darsidagi "nuqtadan oldingi obyekt" qoidasi: Taom.soni da nuqtadan oldin Taom turibdi. Shuning uchun this.#soni — Taom.#soni bilan bir xil.

Qadamma-qadam ko'ring — Keyingi tugmasini bosib, Taom.#soni qayerda o'sishiga qarang:

3.5 Tuzoq: static metodni callback qilib berish

this "nuqtadan oldingi obyekt" bo'lsa, nuqta yo'qolganda nima bo'ladi? this yo'qolishi darsidagi muammo static metodlarda ham bor:

js
class Taom {
  constructor(nom, narx) {
    this.nom = nom;
    this.narx = narx;
  }
  static dan(malumot) {
    return new this(malumot.nom, malumot.narx);
  }
}

const malumotlar = [{ nom: "Osh", narx: 35000 }];
const menyu = malumotlar.map(Taom.dan);
text
TypeError: this is not a constructor

Tarjimasi: "this — konstruktor emas". map Taom.dan ni nuqtasiz, oddiy funksiya sifatida chaqirdi. Klass kodi doim strict — this undefined bo'ldi. new undefined(...) esa mumkin emas.

Ikki tuzatish:

  • Chaqiruvni arrow funksiyaga o'rang: malumotlar.map((m) => Taom.dan(m)). Nuqta saqlanadi. Bu Callback imzosi darsidagi tuzoqdan ham himoya qiladi: map ikkinchi argument sifatida indeksni ham beradi.
  • Yoki static metod ichida this o'rniga klass nomini yozing: new Taom(...).

Ikkinchi yo'l oddiyroq ko'rinadi, lekin this ning foydasi bor — uni Meros darsida ko'ramiz. Shuning uchun birinchi odat yaxshiroq: static metodni callback qilib berishda arrow bilan o'rang.

3.6 static {} bloki

Ba'zan klass e'lon qilinayotganda bir martalik hisob kerak bo'ladi. Masalan, menyudan eng arzon taomni topib, static maydonga yozib qo'yish. Buning uchun static {} bloki bor:

js
class Menyu {
  static taomlar = [
    { nom: "Osh", narx: 35000 },
    { nom: "Lag'mon", narx: 28000 },
    { nom: "Manti", narx: 30000 },
  ];
  static engArzon;

  static {
    const tartib = Menyu.taomlar.toSorted((a, b) => a.narx - b.narx);
    Menyu.engArzon = tartib[0].nom;
    console.log("static blok ishladi");
  }
}

console.log(Menyu.engArzon);

Konsolda:

text
static blok ishladi
Lag'mon

Blok klass e'lonining o'zida, bir marta bajariladi — bitta ham nusxa yaratilmagan bo'lsa ham. U yuqoridan pastga, static maydonlar bilan aralash tartibda ishlaydi: taomlar tayyor, keyin blok. Blok ichida klassning private a'zolariga ham kirish mumkin.

Amalda static {} kam uchraydi — ko'pincha oddiy static maydon yetarli. Lekin kutubxona kodida uchratasiz, shuning uchun tanib qo'ying.

4. Factory metod

4.1 Konstruktorning cheklovi

Konstruktor bitta va uning imzosi bitta: new Taom(nom, narx). Ma'lumot esa har xil shaklda keladi:

  • Obyekt bo'lib — { nom: "Osh", narx: 35000 } (JSON asoslari darsida JSON.parse aynan shunday obyekt qaytaradi).
  • Satr bo'lib — Otabek daftarga yozgan "Osh:35000".

Har safar new Taom(m.nom, m.narx) yoki satrni bo'lib yozish — takrorlanadigan kod. Yechim — factory metod (factory method): yangi nusxani yasab qaytaradigan static metod. "Factory" — "zavod": xom ashyo kiradi, tayyor mahsulot chiqadi.

js
class Taom {
  constructor(nom, narx) {
    this.nom = nom;
    this.narx = narx;
  }

  static dan(malumot) {
    return new Taom(malumot.nom, malumot.narx);
  }

  static satrdan(satr) {
    const [nom, narx] = satr.split(":");
    return new Taom(nom, Number(narx));
  }
}

const osh = Taom.dan({ nom: "Osh", narx: 35000 });
const manti = Taom.satrdan("Manti:30000");
console.log(osh, manti);

Konsolda:

text
Taom { nom: 'Osh', narx: 35000 } Taom { nom: 'Manti', narx: 30000 }

Har factory metodning nomi qaysi manbadan yaratilishini aytadi: dan — obyektdan, satrdan — satrdan. Ichida esa yagona yo'l — new Taom(...). Konstruktor o'zgarsa, faqat shu metodlar moslanadi; ularni chaqirgan yuzlab joy esa o'zgarmaydi.

Array.from(...), Object.fromEntries(...) — xuddi shu naqsh: "from" — "-dan". Biz o'zbekcha nom bilan yozdik: Taom.dan(...).

4.2 Qaysi biri qachon?

Holat Tanlov
Hamma ma'lumot qo'lda, aniq new Taom("Osh", 35000)
Ma'lumot obyekt yoki satr bo'lib keladi Taom.dan(...), Taom.satrdan(...)
Yaratishdan oldin tayyorlash kerak factory metod (ichida tekshirish yoki tozalash)

Factory metodni Closure amaliyotda darsidagi "fabrika funksiya" bilan adashtirmang. U yerda oddiy funksiya obyekt qaytarardi. Bu yerda esa — klassning static metodi, u klass nusxasini qaytaradi. G'oya bir xil: "yaratish ishini bitta joyga yig'ish". Bu g'oya JS'da dizayn naqshlari darsida alohida naqsh sifatida qaytadi.

Tekshirib ko'ring: Taom.satrdan("Osh:35000").narx qanday turda bo'ladi? Number(...) ni unutsak-chi?

Javob

Number bilan — son, 35000. Usiz — satr, "35000": split doim satrlar qaytaradi. Keyin osh.narx + 5000 qo'shish o'rniga yopishtiradi — "350005000". Factory metodning foydasi shu: tur o'zgartirishni bir joyda qilib, hamma chaqiruvchini bu tuzoqdan qutqaradi.

5. Ko'p uchraydigan xatolar

5.1 Setter ichida o'ziga yozish

js
class Taom {
  set narx(yangi) {
    this.narx = yangi;
  }
}

new Taom().narx = 35000;
text
RangeError: Maximum call stack size exceeded

Tarjimasi: "chaqiruvlar stekining eng katta o'lchamidan oshib ketildi" (Rekursiya asoslari). this.narx = yangi — yana narx ga yozish, ya'ni yana shu setter. U yana o'zini chaqiradi — cheksiz. Tuzatish: qiymatni boshqa joyda saqlang — this.#narx = yangi.

5.2 Maydon getterni yopib qo'yadi

js
class Taom {
  narx = 0;
  get narx() {
    return 35000;
  }
}

console.log(new Taom().narx); // 0

Xato xabari yo'q, getter esa umuman ishlamadi. narx = 0 — maydon, u har nusxaning o'ziga yoziladi. Getter esa prototipda. Qidiruv nusxadan boshlanadi va 0 ni darhol topadi — prototipgacha yetmaydi (Shadowing). Tuzatish: bitta nomni ikki marta ishlatmang; qiymat — #narx da, getter — narx.

5.3 static a'zoni nusxadan chaqirish

js
class Taom {
  static dan(m) {
    return new Taom(m.nom, m.narx);
  }
}

const osh = new Taom();
osh.dan({ nom: "Manti", narx: 30000 });
text
TypeError: osh.dan is not a function

dan — klassda, nusxada yo'q. Tuzatish: Taom.dan(...). Teskari xato ham bor: oddiy metodni klassdan chaqirish — Taom.qosh() — Taom.qosh is not a function. Xabardagi nuqtadan oldingi so'zga qarang: nusxami yoki klassmi?

5.4 static metodni callback qilib berish

«static metodni callback qilib berish» bo'limidagi TypeError: this is not a constructor. Tuzatish: (m) => Taom.dan(m).

6. Mashqlar

1-mashq (oson): Bron va odam soni

«Bahor» bron formasida odam soni 1 dan 20 gacha. Bron klassini yozing: ism va odam maydoni, odam — #odam da, getter va setter bilan. Setter faqat 1–20 oralig'idagi butun sonni qabul qilsin, aks holda console.warn bilan ogohlantirsin. Ishora: butun sonni Number.isInteger tekshiradi.

Yechim
js
class Bron {
  #odam = 1;

  constructor(ism, odam) {
    this.ism = ism;
    this.odam = odam;
  }

  get odam() {
    return this.#odam;
  }

  set odam(soni) {
    if (!Number.isInteger(soni) || soni < 1 || soni > 20) {
      console.warn(`Odam soni 1–20 bo'lsin: ${soni}`);
      return;
    }
    this.#odam = soni;
  }
}

const bron = new Bron("Malika", 4);
bron.odam = 25;
bron.odam = 6;
console.log(bron.odam);

Konsolda:

text
Odam soni 1–20 bo'lsin: 25
6

#odam = 1 — boshlang'ich qiymat. Konstruktorga noto'g'ri son berilsa (new Bron("Ali", 0)), setter uni rad etadi va nusxada 1 qoladi.

2-mashq (o'rta): Bronlar hisoblagichi va factory

Bron ga qo'shing:

  1. static #soni va static get soni() — nechta bron yaratilgani.
  2. static satrdan(satr) — "Ali;4" kabi satrdan bron yasasin. Ishora: split(";") va Number(...).
Yechim
js
class Bron {
  static #soni = 0;

  constructor(ism, odam) {
    this.ism = ism;
    this.odam = odam;
    Bron.#soni++;
  }

  static get soni() {
    return Bron.#soni;
  }

  static satrdan(satr) {
    const [ism, odam] = satr.split(";");
    return new Bron(ism, Number(odam));
  }
}

const bronlar = ["Ali;4", "Malika;2"].map((s) => Bron.satrdan(s));
console.log(bronlar[1]); // Bron { ism: 'Malika', odam: 2 }
console.log(Bron.soni); // 2

Hisoblagich konstruktorda oshadi, shuning uchun satrdan orqali yaratilganlar ham sanaladi — factory ichida ham new Bron bor. map ga Bron.satrdan ni arrow bilan o'rab berdik — ichida this yo'q bo'lsa ham bu yaxshi odat.

3-mashq (qiyin): Savat

Savat klassini yozing:

  • #taomlar — { nom, narx, soni } obyektlari massivi; qosh(nom, narx) — taom bor bo'lsa sonini oshirsin, yo'q bo'lsa yangisini qo'shsin.
  • get jami — summa, get bosh — savat bo'shmi (true/false).
  • static CHEGIRMA_CHEGARASI = 100000 va get chegirma — jami shu chegaradan oshsa, 10%, aks holda 0.

Ishora: taomni find bilan qidiring, summani reduce bilan yig'ing.

Yechim
js
class Savat {
  static CHEGIRMA_CHEGARASI = 100000;
  #taomlar = [];

  qosh(nom, narx) {
    const bor = this.#taomlar.find((t) => t.nom === nom);
    if (bor) {
      bor.soni++;
    } else {
      this.#taomlar.push({ nom, narx, soni: 1 });
    }
  }

  get jami() {
    return this.#taomlar.reduce((s, t) => s + t.narx * t.soni, 0);
  }

  get bosh() {
    return this.#taomlar.length === 0;
  }

  get chegirma() {
    if (this.jami <= Savat.CHEGIRMA_CHEGARASI) {
      return 0;
    }
    return this.jami / 10;
  }
}

const savat = new Savat();
console.log(savat.bosh); // true
savat.qosh("Osh", 35000);
savat.qosh("Osh", 35000);
savat.qosh("Manti", 30000);
savat.qosh("Ko'k choy", 5000);
console.log(savat.jami, savat.chegirma); // 105000 10500

chegirma getteri boshqa getterni — this.jami ni — o'qiydi. Getterlar bir-biriga tayanishi normal. #taomlar dagi obyektlarni bor.soni++ bilan o'zgartirish xavfsiz: ular tashqaridan ko'rinmaydi, faqat Savat ga tegishli. Savat.CHEGIRMA_CHEGARASI — klass nomi orqali, chunki u static.

4-mashq: Vazifalar qadami — Vazifa ga o'zgarmas va factory

Private # maydon darsida refactor/vazifa-klassi branch'i main ga qo'shildi. Bu va keyingi to'rt dars uchun yangi branch oching: git switch -c refactor/vazifalar-royxati. Oxirida vazifalar da VazifalarRoyxati klassi paydo bo'ladi.

Hozir Vazifa shunday (metod tanalari qisqartirilgan):

js
class Vazifa {
  id;
  bajarildi;
  #matn;

  constructor(id, matn, bajarildi = false) { ... }
  get matn() { ... }
  matnniOzgartir(yangi) { ... } // bo'sh yoki 100 dan uzun — false
  toJSON() { ... }
  almashtir() { ... }
}

Ikki kichik muammo bor:

  1. 100 soni ikki joyda yozilgan: matnniOzgartir ichida (toza.length > 100) va tahrirQatori da (kiritma.maxLength = 100). Bu — "sehrli son". Biri o'zgarib, ikkinchisi unutilsa, maydon 150 belgini qabul qiladi, Vazifa esa rad etadi.
  2. holat oddiy obyektdan nusxa yasashni o'zi biladi: new Vazifa(v.id, v.matn, v.bajarildi) — uch maydon qo'lda sanab yozilgan. 11-qismda vazifalar saqlangan joydan ham oddiy obyekt bo'lib keladi — aynan toJSON qaytaradigan { id, matn, bajarildi } shaklida. "Oddiy obyektdan nusxa" yo'li bitta bo'lsin va Vazifa ning o'zida tursin.

Vazifa: static MAKS_UZUNLIK = 100 va static dan(malumot) factory metodini qo'shing, ikkala 100 ni va holat dagi new Vazifa(...) ni ularga almashtiring. Xulq o'zgarmasin.

Qo'shimcha savol: matnniOzgartir ni set matn(yangi) setter'iga aylantirish kerakmi?

Yechim

Vazifa ning boshi:

js
class Vazifa {
  static MAKS_UZUNLIK = 100;

  id;
  bajarildi;
  #matn;

  constructor(id, matn, bajarildi = false) {
    this.id = id;
    this.#matn = matn;
    this.bajarildi = bajarildi;
  }

  static dan(malumot) {
    return new Vazifa(malumot.id, malumot.matn, malumot.bajarildi);
  }

matnniOzgartir ichida bitta o'zgarish:

js
if (toza === "" || toza.length > Vazifa.MAKS_UZUNLIK) {

Holatning boshi — muzlatilgan oddiy ma'lumot, factory orqali:

js
const holat = {
  vazifalar: boshlangichVazifalar.map(
    (malumot) => Vazifa.dan(malumot),
  ),
  filtr: "hammasi",
  tahrirId: null,
  // qoldi getter'i — avvalgidek
};

Va tahrirQatori ichida:

js
kiritma.maxLength = Vazifa.MAKS_UZUNLIK;

Nimaga e'tibor bering:

  • Vazifa.MAKS_UZUNLIK — klass nomi orqali, metod ichida ham. Qoida endi bitta joyda.
  • .map((malumot) => Vazifa.dan(malumot)) — arrow bilan, «static metodni callback qilib berish» bo'limidagi odat.
  • toJSON va dan — juftlik: biri nusxani oddiy obyektga aylantiradi, ikkinchisi oddiy obyektdan nusxa yasaydi. 11-qismda saqlash shu ikkisiga tayanadi.
  • index.html dagi maxlength="100" qoladi: HTML klassni o'qiy olmaydi. Brauzer foydalanuvchini kiritish paytida to'xtatadi, matnniOzgartir esa boshqa yo'l bilan kelgan matndan himoya qiladi. Ikkalasi bir xil bo'lishi kerakligini Vazifa ga izoh qilib yozib qo'ying.

Setter kerakmi? Yo'q. matnniOzgartir true/false qaytaradi — chaqiruvchi o'zgarish qabul qilinganini biladi. Setter esa natija qaytara olmaydi: vazifa.matn = "" ifodasining qiymati doim o'ng tomondagi qiymat, setter nima qaytarishidan qat'i nazar. Rad etilgan matn haqida setter xabar berishining yagona yo'li — xato tashlash (throw). Shuning uchun natija kerak bo'lgan o'zgartirish — metod. Bu «Getter qachon kerak, qachon metod?» bo'limidagi tanlovning setter'dagi juftligi.

Xulq o'zgarmaganini tekshiring:

  • Ochilganda — uch vazifa, "2 ta vazifa qoldi, 1 ta bajarildi".
  • "DOM darsini takrorlash" ni tahrirlang: DOM takrori (ikki chetida bo'sh joy) yozib Enter — ro'yxatda "DOM takrori", "O'zgarish saqlandi.", fokus «Tahrirlash» da.
  • Tahrir maydonida 100 tadan ortiq belgi yozib bo'lmaydi.
  • Yangi vazifa, belgilash, o'chirish, filtrlar — hammasi avvalgidek.

Commit: Vazifalar: MAKS_UZUNLIK va Vazifa.dan qo'sh.

7. Real ishda

  • Validatsiya qatlami. Domen klasslarida (Foydalanuvchi, Buyurtma) setter qiymatni tekshiradi, getter esa hisoblanadigan maydonlarni beradi: buyurtma.jami, foydalanuvchi.toliqIsm.
  • Factory metodlar — deyarli har kutubxonada: Array.from, Object.fromEntries, Date.now(), Promise.resolve (11-qismda). Backend'da serverdan kelgan obyektni klass nusxasiga aylantirish — Foydalanuvchi.dan(json) — har kungi ish.
  • O'zgarmaslar. static MAKS_HAJM, static VERSIYA — klass bilan bog'liq qoidalar bir joyda turadi, kod bo'ylab sochilmaydi.
  • TypeScript va freymvorklar. TypeScript'da (15-qism) static va getter/setter aynan shunday yoziladi. NestJS (30-qism) va boshqa backend freymvorklarida static factory metodlar ko'p uchraydi.
  • Intervyu. "static metod nima va qachon ishlatiladi?", "getter va oddiy metodning farqi?" — junior va o'rta darajadagi klassik savollar. Javob: static — nusxa kerak bo'lmagan ish; getter — qiymat, xususiyatdek o'qiladi.

Xulosa

  • get/set klassda — xususiyat o'qilganda va yozilganda ishlaydigan funksiyalar; ular prototype da, enumerable: false.
  • #maydon + getter + setter — qo'riqlangan xususiyat; setter yo'q bo'lsa — faqat o'qiladigan.
  • Getter tez, yon ta'sirsiz va ot bilan atalgan bo'lsin. Setter ichida o'ziga yozmang — cheksiz rekursiya. Natija qaytarishi kerak bo'lgan o'zgartirish — metod.
  • static a'zo klassning o'zida: Taom.VALYUTA, Taom.dan(). Nusxadan topilmaydi.
  • static metod ichida this — klass; callback qilib berishda arrow bilan o'rang.
  • Factory metod (Taom.dan, Array.from) — har xil shakldagi ma'lumotdan nusxa yasashning bitta joyi.

Keyingi dars: Meros: extends va super — bir klassni boshqasi ustiga quramiz: ota klass xulqini olib, kerak joyini qayta yozamiz.

Manbalar

  • MDN: "get", "set", "static", "Static initialization blocks" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "ClassDefinitionEvaluation" — tc39.es/ecma262
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript klassida getter, setter va static: nusxaniki va klassning o'ziniki — IlmHamroh