IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur40/65-dars21 daqiqa
Mundarija (31)

JavaScript klass merosi: extends va super bilan ota klass ustiga qurish

Qisqacha: class Ichimlik extends Taom — Ichimlik klassi Taom ning hamma metodlarini meros oladi va o'zinikini qo'shadi. Bola klass konstruktorida this dan oldin super(...) chaqirilishi shart: nusxani ota klass yaratadi. Metodni qayta yozsangiz (override), ota versiyasiga super.metod() bilan murojaat qilasiz.

Bu darsda

  • extends bilan bir klassni boshqasi ustiga qura olasiz.
  • super(...) nega this dan oldin kelishi kerakligini tushuntira olasiz.
  • Metodni qayta yozib, ota versiyasini super.metod() bilan chaqira olasiz.
  • Meros ikki zanjir — nusxalar va klasslar — bo'ylab qanday ishlashini chizib bera olasiz.
  • Array, Error va EventTarget dan meros olishni va ularning o'ziga xos tomonlarini bilasiz.

Oldin bilishingiz kerak: Getter/setter va static a'zolar, Prototip zanjiri, Object.create va instanceof, Custom events va EventTarget.

1. Nega bu kerak?

«Bahor» menyusida Taom klassi bor: nomi, narxi va menyu qatorini yasaydigan tavsif() metodi. Endi Jasur aka ichimliklarni alohida ko'rsatmoqchi. Ichimlikda ham nom, narx va tavsif bor. Lekin ikkita qo'shimcha: hajmi (0.5 l) va issiq-sovuqligi.

Birinchi fikr — Taom ni nusxalab, Ichimlik deb nomlash va ikki maydon qo'shish. Ishlaydi. Lekin bir oydan keyin Jasur aka "narxga so'm qo'shib yozilsin" desa, ikki joyni tuzatish kerak bo'ladi. Uchinchi tur — Shirinlik — qo'shilsa, uch joyni. Bir joyini unutsangiz — menyuda ikki xil format chiqadi.

Hayotdan o'xshatish. Toshkent ko'chalaridagi oddiy Cobalt va Cobalt-taksi. Taksi — o'sha mashina: o'sha dvigatel, o'sha rul. Faqat ustida shashka, ichida hisoblagich. Zavod taksi uchun mashinani noldan loyihalamaydi — tayyor modelni olib, kerakli qismini qo'shadi. Dvigatel yaxshilansa, taksi ham avtomatik yaxshilanadi.

Klasslarda bu meros (inheritance) deyiladi: yangi klass mavjud klassning hamma xulqini oladi va faqat farq qiladigan joyini o'zi yozadi. Prototip zanjiri va Object.create darslarida merosning ichki mexanizmini ko'rgansiz. Bugun uning klassdagi qulay yozuvi — extends va super.

2. extends: hamma narsa meros

2.1 Bo'sh bola klass

Eng kichik meros — tanasi bo'sh klass:

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

  tavsif() {
    return `${this.nom} — ${this.narx} so'm`;
  }
}

class Ichimlik extends Taom {}

const choy = new Ichimlik("Ko'k choy", 5000);
console.log(choy.tavsif());
console.log(choy instanceof Ichimlik, choy instanceof Taom);

Konsolda:

text
Ko'k choy — 5000 so'm
true true

Ichimlik da bitta ham qator yo'q, lekin uning nusxasi konstruktorni ham, tavsif() ni ham ishlatdi. extends Taom — "Ichimlik — Taom ning bir turi" degani.

Ikki atama: ota klass (parent class) — meros beradigan klass (Taom), bola klass (child class) — meros oladigan klass (Ichimlik). Kitoblarda "superclass" va "subclass" ham uchraydi — ma'nosi bir xil.

choy instanceof Taom — true. instanceof zanjir bo'ylab tekshiradi: choy — ichimlik, ichimlik esa — taom. Har ichimlik taom, lekin har taom ichimlik emas.

2.2 Bola klass konstruktori: super()

Endi Ichimlik ga hajm qo'shamiz. Buning uchun o'z konstruktori kerak:

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

class Ichimlik extends Taom {
  constructor(nom, narx, hajm) {
    super(nom, narx); // ota konstruktori ishlaydi
    this.hajm = hajm; // keyin o'zimizniki
  }
}

const choy = new Ichimlik("Ko'k choy", 5000, 0.5);
console.log(choy);
// Ichimlik { nom: "Ko'k choy", narx: 5000, hajm: 0.5 }

super(nom, narx) — ota klass konstruktorini chaqirish. nom va narx ni qanday saqlashni Taom biladi, biz uni takrorlamaymiz. Ichimlik faqat o'z yangiligini — hajm ni — qo'shadi.

Node chiqishida nom: "Ko'k choy" qo'shtirnoqda — chunki satr ichida apostrof bor. Node satrni bitta tirnoqqa o'rasa, apostrof uni buzardi, shuning uchun qo'shtirnoqni tanladi. Qiymat o'sha.

2.3 Qoida: this dan oldin super

Tartibni almashtirib ko'ramiz:

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

class Ichimlik extends Taom {
  constructor(nom, narx, hajm) {
    this.hajm = hajm;
    super(nom, narx);
  }
}

new Ichimlik("Ko'k choy", 5000, 0.5);
text
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

Tarjimasi: "hosila (bola) klassda this ga murojaat qilishdan yoki konstruktordan qaytishdan oldin ota konstruktorini chaqirish shart". Xato turi — ReferenceError: this hali mavjud emas.

Nega? Konstruktor funksiya va new darsida new ning to'rt qadamini ko'rgan edik: birinchi qadam — bo'sh obyekt yaratish. Oddiy klassda bu obyektni new yaratadi. Bola klassda esa yaratmaydi — bu ishni ota klassga qoldiradi. Obyektni super() qaytaradi va shundagina u this ga aylanadi. super() gacha this — bo'sh joy.

Xabarning ikkinchi yarmi — "or returning" — super() ni umuman unutgan holat. Konstruktorda this ga tegmasangiz ham, super() siz tugatsangiz, xuddi shu xato chiqadi: qaytariladigan obyekt yo'q.

Bola klassda konstruktor yozmasangiz-chi? «Bo'sh bola klass» bo'limidagi Ichimlik ga o'xshab. Unda JavaScript yashirin konstruktor qo'shadi — barcha argumentlarni otaga uzatadigan:

js
constructor(...args) {
  super(...args);
}

Shuning uchun bo'sh Ichimlik ham ishladi. Konstruktor faqat qo'shimcha ish kerak bo'lganda yoziladi.

Tekshirib ko'ring: Quyidagi konstruktor ishlaydimi? constructor(nom, narx) { const toliq = nom.trim(); super(toliq, narx); }

Javob

Ha. Qoida "konstruktorning birinchi qatori super bo'lsin" emas — "this dan oldin super bo'lsin". this ga tegmaydigan tayyorgarlik (argumentni tozalash, hisoblash) super dan oldin bo'lishi mumkin.

2.4 Maydonlar qachon paydo bo'ladi?

Class maydonlari darsida maydonlar konstruktordan oldin ishga tushishini ko'rgan edik. Bola klassda bitta aniqlik bor: bola maydonlari super() qaytgandan keyin yoziladi. this aynan shu paytda paydo bo'ladi. Qadamma-qadam qarang:

Tartib: ota maydonlari → ota konstruktori → bola maydonlari → bola konstruktorining qolgani. Konsoldagi xususiyatlar tartibi buni tasdiqlaydi: narx, nom, issiq, hajm.

3. Metodni qayta yozish va super.metod()

3.1 Override

choy.tavsif() hozir "Ko'k choy — 5000 so'm" qaytaradi. Ichimlik uchun hajm ham kerak: "Ko'k choy — 5000 so'm, 0.5 l". Bola klassda xuddi shu nomli metod yozamiz:

js
class Taom {
  constructor(nom, narx) {
    this.nom = nom;
    this.narx = narx;
  }
  tavsif() {
    return `${this.nom} — ${this.narx} so'm`;
  }
}

class Ichimlik extends Taom {
  constructor(nom, narx, hajm) {
    super(nom, narx);
    this.hajm = hajm;
  }
  tavsif() {
    return `${super.tavsif()}, ${this.hajm} l`;
  }
}

const menyu = [
  new Taom("Osh", 35000),
  new Ichimlik("Ko'k choy", 5000, 0.5),
];
menyu.forEach((t) => console.log(t.tavsif()));

Konsolda:

text
Osh — 35000 so'm
Ko'k choy — 5000 so'm, 0.5 l

Qayta yozish (override) — bola klassda ota metodi bilan bir xil nomli metod yozish. Endi choy.tavsif() bola versiyasini chaqiradi. Nega? Qidiruv prototip zanjiri bo'ylab pastdan yuqoriga boradi: avval Ichimlik.prototype — u yerda tavsif bor, qidiruv to'xtaydi. Taom.prototype dagi versiyaga navbat kelmaydi. Bu — o'sha shadowing, faqat metodlar uchun.

3.2 super.tavsif() — ota versiyasini chaqirish

Bola tavsif ota formatini takrorlamadi. U super.tavsif() ni chaqirdi — "otamning tavsif i nima qaytarsa, o'shani ber" — va oxiriga hajm qo'shdi. Jasur aka narx formatini o'zgartirsa, faqat Taom.tavsif tuzatiladi — ichimliklar ham avtomatik yangilanadi.

super ikki xil ishlatiladi:

Yozuv Qayerda Nima qiladi
super(...) faqat bola konstruktorida ota konstruktorini chaqiradi
super.metod(...) har qanday metodda ota prototipidagi metodni chaqiradi, this — o'sha nusxa

Ikkinchisida this saqlanadi: super.tavsif() ichida this.nom — choyning nomi. Ota metodi "boshqa obyektda" emas, xuddi shu nusxada ishlaydi.

Bitta e'tibor: forEach callback'ida biz t ning qaysi klassdan ekanini so'ramadik. Har nusxa o'z tavsif ini o'zi bildi. Bir xil chaqiruv — har xil xulq. Bu polimorfizm deyiladi, uni OOP tamoyillari darsida batafsil ko'ramiz.

Tekshirib ko'ring: Ichimlik dagi tavsif ichida super.tavsif() o'rniga this.tavsif() yozilsa nima bo'ladi?

Javob

this.tavsif() — yana Ichimlik ning tavsif i, ya'ni o'zi. U yana o'zini chaqiradi — cheksiz rekursiya va RangeError: Maximum call stack size exceeded. super. aynan zanjirning bitta yuqorisidan qidirishni boshlaydi, shuning uchun o'ziga qaytib kelmaydi.

3.3 Getter va static ham meros

Meros faqat oddiy metodlar uchun emas. Oldingi darsdagi getter, setter va static a'zolar ham o'tadi:

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

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

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

class Ichimlik extends Taom {}

const kompot = Ichimlik.dan({ nom: "Kompot", narx: 8000 });
console.log(kompot); // Ichimlik { nom: 'Kompot', narx: 8000 }
console.log(Ichimlik.VALYUTA); // so'm

Ichimlik.dan — Taom dan meros olingan static metod. Diqqat qiling: natija Ichimlik, Taom emas. Chunki dan ichida new this(...). Ichimlik.dan(...) da nuqtadan oldin Ichimlik turibdi, demak this — Ichimlik.

O'tgan darsda "static metodda this ning foydasi Meros darsida ko'rinadi" degan edik — mana u. new Taom(...) yozilganda bola klasslar ham Taom nusxasini olardi. new this(...) esa "kim chaqirgan bo'lsa, o'shaning nusxasi" degani.

4. Ichkarida: ikki zanjir

extends ikki havolani bog'laydi. Birinchisi nusxalar uchun, ikkinchisi — static a'zolar uchun:

js
class Taom {}
class Ichimlik extends Taom {}

const proto = Object.getPrototypeOf(Ichimlik.prototype);
console.log(proto === Taom.prototype); // true
console.log(Object.getPrototypeOf(Ichimlik) === Taom); // true
flowchart TB
  T["Taom<br/>static: VALYUTA, dan()"]
  I["Ichimlik"]
  TP["Taom.prototype<br/>tavsif()"]
  IP["Ichimlik.prototype<br/>tavsif() — qayta yozilgan"]
  C["choy — nusxa<br/>nom, narx, hajm"]
  I -- "[[Prototype]]" --> T
  IP -- "[[Prototype]]" --> TP
  C -- "[[Prototype]]" --> IP
  I -. ".prototype" .-> IP
  T -. ".prototype" .-> TP

Diagrammada ikki zanjirni toping (uzluksiz [[Prototype]] o'qlari). Birinchisi — nusxalar zanjiri: choy → Ichimlik.prototype → Taom.prototype. choy.tavsif() shu yo'l bilan qidiriladi. Ikkinchisi — klasslar zanjiri: Ichimlik → Taom. Ichimlik.dan va Ichimlik.VALYUTA shu yo'l bilan topiladi. Uzuq chiziqlar — har klassning o'z prototype iga havolasi.

super.tavsif() ham shu diagramma bo'yicha ishlaydi: u Ichimlik.prototype ning bitta yuqorisidan — Taom.prototype dan — qidiradi. super bu "yuqori"ni metod qaysi klassda yozilganiga qarab topadi, chaqirilgan obyektga qarab emas. Bu nozik farq class ichidan darsida qaytadi.

Object.create darsida xuddi shunday zanjirni qo'lda qurgan edik. extends — o'sha mexanizmning qisqa yozuvi, yangi mexanizm emas.

4.1 Zanjir uzaysa

Bola klassdan ham meros olish mumkin. Ko'k choy — ichimlikning bir turi, ichimlik — taomning:

js
class Taom {
  tavsif() {
    return "taom";
  }
}
class Ichimlik extends Taom {
  tavsif() {
    return `${super.tavsif()} → ichimlik`;
  }
}
class IssiqIchimlik extends Ichimlik {
  tavsif() {
    return `${super.tavsif()} → issiq`;
  }
}

const choy = new IssiqIchimlik();
console.log(choy.tavsif()); // taom → ichimlik → issiq
console.log(choy.constructor.name); // IssiqIchimlik
console.log(choy instanceof Taom); // true

Har super.tavsif() bitta qavat yuqoriga chiqdi va natija pastdan yuqoriga yig'ildi. choy.constructor.name — nusxa qaysi klassdan yaratilganini aytadi: constructor xususiyati prototipdan keladi (Konstruktor funksiya va new).

Texnik jihatdan zanjir istalgancha uzun bo'lishi mumkin. Amalda esa ikki-uch qavatdan oshsa, kodni o'qish qiyinlashadi. choy.tavsif() nima qaytarishini bilish uchun uchta klassni yuqoridan pastgacha o'qib chiqish kerak. Bitta qavatdagi o'zgarish pastdagi hamma klassga ta'sir qiladi — va siz ularning hammasini eslab yurolmaysiz.

Shuning uchun tajribali jamoalarda qoida bor: meros zanjiri sayoz bo'lsin. Chuqur ierarxiya kerak bo'lib qolsa — bu ko'pincha noto'g'ri tanlov belgisi. Muqobilini keyingi darsda ko'ramiz.

Tekshirib ko'ring: IssiqIchimlik dagi tavsif ni o'chirsak, choy.tavsif() nima qaytaradi?

Javob

"taom → ichimlik". Qidiruv IssiqIchimlik.prototype da tavsif ni topmaydi va bir qavat yuqoriga — Ichimlik.prototype ga — chiqadi. U yerdagi versiya super.tavsif() orqali Taom nikini oladi va " → ichimlik" qo'shadi. "→ issiq" qismini yozadigan metod endi yo'q.

5. O'rnatilgan klasslardan meros

extends dan keyin faqat o'zingiz yozgan klass emas, JavaScript'ning o'z klasslari ham kelishi mumkin. Uchta muhimini ko'ramiz.

5.1 Array dan meros

js
class Buyurtmalar extends Array {
  get jami() {
    return this.reduce((s, b) => s + b.narx, 0);
  }
}

const bugun = new Buyurtmalar();
bugun.push(
  { taom: "Osh", narx: 35000 },
  { taom: "Manti", narx: 30000 },
);
console.log(bugun.jami); // 65000

const qimmat = bugun.filter((b) => b.narx > 32000);
console.log(qimmat instanceof Buyurtmalar); // true
console.log(qimmat.jami); // 35000

Massivning hamma metodi ishlaydi, ustiga jami getteri qo'shildi. Qiziq tomoni: filter oddiy massiv emas, Buyurtmalar qaytardi. JavaScript yangi massivni "kim chaqirgan bo'lsa, o'sha klassdan" yasaydi — static dan dagi new this(...) ga o'xshab. Shuning uchun qimmat.jami ham ishladi.

Lekin bitta g'alati joyi bor:

js
class Buyurtmalar extends Array {}

console.log(new Buyurtmalar(3)); // Buyurtmalar(3) [ <3 empty items> ]
console.log(new Buyurtmalar(3).length); // 3

Array konstruktori bitta son olsa, uni uzunlik deb tushunadi va uch "teshikli" bo'sh joy yaratadi (Massivga o'xshash obyektlar va teshikli massivlar). Bola klass bu xulqni ham meros oldi. Massivdan meros olgan klassda konstruktorni o'zgartirish xavfli — filter va map ham uni ichkarida chaqiradi. Amalda massivdan meros kam olinadi; nega — keyingi darsda.

5.2 Error dan meros

Stack trace va xato turlari darsida TypeError, ReferenceError, RangeError ni ko'rdingiz. Ularning hammasi Error klassidan meros oladi. O'z xato turingizni ham shunday yasash mumkin:

js
class TaomTugadiXatosi extends Error {
  constructor(taom) {
    super(`${taom} tugadi`);
    this.name = "TaomTugadiXatosi";
  }
}

const xato = new TaomTugadiXatosi("Manti");
console.log(xato.message); // Manti tugadi
console.log(xato instanceof Error); // true
console.log(String(xato)); // TaomTugadiXatosi: Manti tugadi

super(...) ga xabar matni berildi — Error uni message ga yozadi. this.name — xato turining nomi; konsolda u xabar oldidan chiqadi, xuddi TypeError: kabi.

Bu yerda xatoni faqat yaratdik. Uni "tashlash" (throw) — dasturni to'xtatib, xatoni yuqoriga yuborish — va ushlash (try/catch) xatolar bilan ishlash darslarida. O'z xato klasslaringiz uchun alohida dars ham bor: O'z xato klasslaringiz. Hozir faqat shuni bilib qo'ying: xato turlari ham oddiy klass merosi.

5.3 EventTarget dan meros

Custom events va EventTarget darsining oxirida bitta kodni "hozir tushunish shart emas" deb ko'rsatgan edik. Endi uning har qatorini o'qiy olasiz:

js
class Savat extends EventTarget {
  taomlar = [];

  qosh(taom) {
    this.taomlar.push(taom);
    this.dispatchEvent(
      new CustomEvent("ozgardi", { detail: this.taomlar.length }),
    );
  }
}

const savat = new Savat();
savat.addEventListener("ozgardi", (event) => {
  console.log(`Savatda: ${event.detail} ta`);
});
savat.qosh("Osh");
savat.qosh("Ko'k choy");

Konsolda:

text
Savatda: 1 ta
Savatda: 2 ta
  • extends EventTarget — Savat nusxasida addEventListener va dispatchEvent bor. Ular EventTarget.prototype dan keladi.
  • Konstruktor yozilmagan — yashirin super() ishladi, EventTarget o'z ichki ro'yxatini tayyorladi. O'z konstruktorini yozsangiz, super() ni unutmang.
  • taomlar = [] — bola maydoni, super() dan keyin paydo bo'ladi.
  • this.dispatchEvent(...) — Savat o'zi haqida e'lon qiladi. Kim tinglayotganini bilmaydi.

Bu naqsh — "obyekt o'zgarsa, tinglovchilarga xabar beradi" — JS'da dizayn naqshlari darsida "Kuzatuvchi" nomi bilan qaytadi. EventTarget va CustomEvent Node'da ham bor, shuning uchun kod "Ishga tushir" tugmasi bilan ishlaydi.

6. Meros qachon to'g'ri?

Oddiy tekshiruv bor — "...ning bir turi" savoli. Gapni ovoz chiqarib ayting:

  • "Ichimlik — taomning bir turi". To'g'ri eshitiladi — meros mos.
  • "Taksi — mashinaning bir turi". To'g'ri.
  • "Savat — taomning bir turi". Yo'q! Savatda taomlar bor, lekin u o'zi taom emas.

Ikkinchi holatda meros noto'g'ri tanlov. "Bor" munosabati boshqacha quriladi — bir obyekt boshqasini ichida saqlaydi. Bu keyingi darsning mavzusi: kompozitsiya. U yerda merosning narxini ham ko'ramiz — nega tajribali dasturchilar uni ehtiyot bilan ishlatadi.

Tekshirib ko'ring: class Stol extends Choyxona — «Bahor» stollari uchun yaxshi fikrmi?

Javob

Yo'q. "Stol — choyxonaning bir turi" — noto'g'ri gap. Choyxonada stollar bor. Meros bilan Stol choyxonaning ish vaqti, manzili va menyusini ham olib qo'yardi — ular stolga tegishli emas. To'g'risi: Choyxona ichida stollar ro'yxati.

7. Ko'p uchraydigan xatolar

7.1 super() siz yoki undan oldin this

«this dan oldin super» bo'limidagi ReferenceError: Must call super constructor in derived class.... Xabarda "derived class" (hosila klass) so'zi bo'lsa — darhol bola konstruktorining boshiga qarang. Tuzatish: super(...) ni this ga birinchi murojaatdan oldin chaqiring.

7.2 Ota konstruktori qayta yozilgan metodni chaqiradi

js
class Taom {
  constructor(nom) {
    this.nom = nom;
    console.log(this.tavsif());
  }
  tavsif() {
    return this.nom;
  }
}

class Ichimlik extends Taom {
  hajm = 0.5;
  tavsif() {
    return `${this.nom}, ${this.hajm} l`;
  }
}

new Ichimlik("Ko'k choy"); // Ko'k choy, undefined l

Xato xabari yo'q, lekin hajm undefined. Ota konstruktori bola tavsif ini chaqirdi — u qayta yozilgan. Lekin bola maydonlari (hajm) super() qaytgandan keyin yoziladi — «Maydonlar qachon paydo bo'ladi?» bo'limidagi tartib. Tuzatish: konstruktorda qayta yoziladigan metodni chaqirmang; chiqarish kerak bo'lsa — nusxa tayyor bo'lgach, tashqarida.

7.3 Nom xatosi override o'rniga yangi metod yaratadi

Ichimlik da tavsif o'rniga tafsif yozsangiz, xato chiqmaydi. Bu shunchaki yangi metod bo'ladi, choy.tavsif() esa jimgina ota versiyasini chaqiraveradi. Natija "noto'g'ri formatda" chiqadi va sababini topish qiyin. TypeScript (15-qism) buni override kalit so'zi bilan ushlaydi. Hozircha — nomni ota klassdan nusxalab oling.

7.4 super.metod() ni unutish

Bola tavsif ni qayta yozib, ota formatini qo'lda takrorlasangiz — meros foydasi yo'qoladi. Ota formati o'zgarsa, bola eskisida qoladi. Tuzatish: otaning ishini qayta yozmang — super.tavsif() ni chaqirib, ustiga qo'shing.

8. Mashqlar

1-mashq (oson): Shirinlik

Taom dan meros oladigan Shirinlik klassini yozing: qo'shimcha shakarsiz maydoni (true/false), tavsif() — ota tavsifi va shakarsiz bo'lsa oxirida " (shakarsiz)".

js
class Taom {
  constructor(nom, narx) {
    this.nom = nom;
    this.narx = narx;
  }
  tavsif() {
    return `${this.nom} — ${this.narx} so'm`;
  }
}
Yechim
js
class Taom {
  constructor(nom, narx) {
    this.nom = nom;
    this.narx = narx;
  }
  tavsif() {
    return `${this.nom} — ${this.narx} so'm`;
  }
}

class Shirinlik extends Taom {
  constructor(nom, narx, shakarsiz) {
    super(nom, narx);
    this.shakarsiz = shakarsiz;
  }
  tavsif() {
    const qoshimcha = this.shakarsiz ? " (shakarsiz)" : "";
    return super.tavsif() + qoshimcha;
  }
}

const halva = new Shirinlik("Halva", 12000, true);
const pishiriq = new Shirinlik("Pishiriq", 9000, false);
console.log(halva.tavsif());
console.log(pishiriq.tavsif());

Konsolda:

text
Halva — 12000 so'm (shakarsiz)
Pishiriq — 9000 so'm

Shart bo'yicha qo'shimcha bo'sh satr bo'lishi mumkin — super.tavsif() + "" ota natijasini o'zgarishsiz qaytaradi. Halva va pishiriq narxlari — misol uchun, «Bahor» menyusida ular yo'q.

2-mashq (o'rta): VIP bron

Bron klassida ism, odam va narx() metodi bor: har odamga 10 000 so'm xizmat haqi. VipBron yozing: u alohida xona uchun qo'shimcha 50 000 so'm oladi. narx() ni qayta yozing va super.narx() dan foydalaning.

js
class Bron {
  constructor(ism, odam) {
    this.ism = ism;
    this.odam = odam;
  }
  narx() {
    return this.odam * 10000;
  }
}
Yechim
js
class Bron {
  constructor(ism, odam) {
    this.ism = ism;
    this.odam = odam;
  }
  narx() {
    return this.odam * 10000;
  }
}

class VipBron extends Bron {
  static XONA_NARXI = 50000;

  narx() {
    return super.narx() + VipBron.XONA_NARXI;
  }
}

const bronlar = [new Bron("Ali", 4), new VipBron("Malika", 6)];
bronlar.forEach((b) => console.log(b.ism, b.narx()));

Konsolda:

text
Ali 40000
Malika 110000

VipBron da konstruktor yo'q — yashirin super(...args) ishladi. forEach ichida qaysi bron ekanini tekshirmadik: har biri o'z narx ini bildi.

3-mashq (qiyin): Hodisa yuboradigan bron daftari

BronDaftari extends EventTarget klassini yozing:

  • #bronlar = [] va qosh(ism, odam) — bronni qo'shib, "qoshildi" hodisasini detail: { ism, odam } bilan yuborsin.
  • get jamiOdam — barcha bronlardagi odamlar soni.

Ikki tinglovchi qo'ying: biri "Yangi bron: Ali, 4 kishi" deb yozsin, ikkinchisi — jami odamni. Ishora: hodisa obyekti va detail — Custom events darsida.

Yechim
js
class BronDaftari extends EventTarget {
  #bronlar = [];

  qosh(ism, odam) {
    this.#bronlar.push({ ism, odam });
    this.dispatchEvent(
      new CustomEvent("qoshildi", { detail: { ism, odam } }),
    );
  }

  get jamiOdam() {
    return this.#bronlar.reduce((s, b) => s + b.odam, 0);
  }
}

const daftar = new BronDaftari();
daftar.addEventListener("qoshildi", (event) => {
  const { ism, odam } = event.detail;
  console.log(`Yangi bron: ${ism}, ${odam} kishi`);
});
daftar.addEventListener("qoshildi", () => {
  console.log(`Jami: ${daftar.jamiOdam} kishi`);
});

daftar.qosh("Ali", 4);
daftar.qosh("Malika", 2);

Konsolda:

text
Yangi bron: Ali, 4 kishi
Jami: 4 kishi
Yangi bron: Malika, 2 kishi
Jami: 6 kishi

#bronlar private — tashqi kod bronni faqat qosh orqali qo'sha oladi, demak har qo'shishda hodisa albatta yuboriladi. Ikkinchi tinglovchi daftar.jamiOdam ni o'qidi: hodisa sinxron, shuning uchun bron allaqachon ro'yxatda.

4-mashq: Vazifalar qadami — tajriba: ro'yxat massivdan meros olsa-chi?

Keyingi darsda vazifalar uchun VazifalarRoyxati klassini yasaymiz — vazifalar to'plamini boshqaradigan obyekt. Bugun birinchi xayolga keladigan yo'lni sinab ko'ring: massivdan meros. Bu tajriba vazifalar ga emas, mashqlar repo'siga tushadi.

mashqlar/10/40-meros/royxat-tajriba.mjs faylini yarating:

js
class Vazifa {
  constructor(id, matn, bajarildi = false) {
    this.id = id;
    this.matn = matn;
    this.bajarildi = bajarildi;
  }
}

class VazifalarRoyxati extends Array {
  get qoldi() {
    return this.filter((v) => !v.bajarildi).length;
  }
}

const royxat = new VazifalarRoyxati();
royxat.push(
  new Vazifa(1, "Non olish"),
  new Vazifa(2, "DOM darsini takrorlash", true),
);
console.log(royxat.qoldi); // 1

Ishlaydi va qisqa. Endi faylning oxiriga uchta "begona" qator qo'shing va har biridan keyin royxat ni konsolga chiqaring:

  1. royxat.push("salom")
  2. royxat[5] = null
  3. royxat.length = 0

Terminalda node royxat-tajriba.mjs bilan ishga tushiring. Har biri nima qildi? vazifalar ilovasi uchun bu xavflimi? Bitta gap bilan javob yozing: "VazifalarRoyxati — massivning bir turimi?"

Yechim
js
class Vazifa {
  constructor(id, matn, bajarildi = false) {
    this.id = id;
    this.matn = matn;
    this.bajarildi = bajarildi;
  }
}

class VazifalarRoyxati extends Array {
  get qoldi() {
    return this.filter((v) => !v.bajarildi).length;
  }
}

const royxat = new VazifalarRoyxati();
royxat.push(new Vazifa(1, "Non olish"));

royxat.push("salom");
console.log(royxat.length, royxat.qoldi);
royxat[5] = null;
console.log(royxat.length);
royxat.length = 0;
console.log(royxat.length, royxat.qoldi);

Konsolda:

text
2 2
6
0 0

Uchala qator ham xatosiz o'tdi:

  1. "salom" vazifa emas, lekin ro'yxatga kirdi va hatto "faol" deb sanaldi: satrda bajarildi yo'q, !undefined — true.
  2. royxat[5] = null — uzunlik 6 ga sakradi, orada teshiklar paydo bo'ldi.
  3. royxat.length = 0 — hamma vazifa bir zumda yo'qoldi.

Array dan meros bilan VazifalarRoyxati massivning hamma imkoniyatlarini oldi — 30 dan ortiq metod va to'g'ridan-to'g'ri indeks bilan yozish. Ularning har biri ilovaning qoidalarini (faqat Vazifa, id takrorlanmaydi) chetlab o'tadigan eshik. yangiId generatoriga esa massivda joy yo'q — u yana tashqarida qoladi.

Javob: yo'q, VazifalarRoyxati massivning bir turi emas. Unda vazifalar bor. «Meros qachon to'g'ri?» bo'limidagi tekshiruvdan o'tmadi. Keyingi darsda boshqa yo'lni tanlaymiz: massivni klass ichiga yashirib, tashqariga faqat kerakli amallarni chiqaramiz.

Commit (mashqlar repo): 10/40: VazifalarRoyxati extends Array tajribasi.

9. Real ishda

  • Xato ierarxiyasi. Backend'da class TopilmadiXatosi extends Error, class RuxsatYoqXatosi extends Error — server xato turiga qarab mijozga 404 yoki 403 javob qaytaradi (25-qism, Express).
  • Web Components. Brauzerda o'z HTML tegingizni yaratish class TaomKarta extends HTMLElement bilan boshlanadi (Web Components). Bu yerda super() majburiy va konstruktor qoidalari aynan shu darsdagidek.
  • Node.js ichida. Node'dagi server, fayl oqimlari va boshqa ko'p obyektlar EventEmitter degan klassdan meros oladi — EventTarget ning Node'dagi "qarindoshi". Shuning uchun ularning hammasida bir xil on(...) va emit(...) metodlari bor. Node'ni 24-qismda chuqur o'rganamiz, hozir bilish shart emas — faqat meros u yerda ham asosiy g'oya ekanini eslab qoling.
  • Freymvorklar. Eski React kodida class Tugma extends React.Component, NestJS'da xato va filtr klasslari — meros. Zamonaviy React funksiyalarga o'tgan, lekin eski loyihalarda klass komponentlarni o'qishingiz kerak bo'ladi (17-qism).
  • Intervyu. "super() ni nega this dan oldin chaqirish kerak?", "extends ichkarida nima qiladi?", "super.method() va this.method() farqi?" — o'rta darajadagi tez-tez beriladigan savollar. Bu darsdagi ikki zanjir diagrammasini chizib bersangiz — mavzuni ichidan bilishingiz ko'rinadi.

Xulosa

  • class B extends A — B nusxalari A ning metodlarini, B ning o'zi esa A ning static a'zolarini meros oladi.
  • Bola konstruktorida this dan oldin super(...) — obyektni ota klass yaratadi. Konstruktor yozilmasa, yashirin super(...args) ishlaydi.
  • Tartib: ota maydonlari → ota konstruktori → bola maydonlari → bola konstruktorining qolgani.
  • Override — bir xil nomli metod; ota versiyasi — super.metod(), this saqlanadi.
  • Array, Error, EventTarget dan ham meros olish mumkin; har birining o'ziga xos tomoni bor.
  • Meros faqat "...ning bir turi" munosabatida. "Ichida bor" bo'lsa — boshqa yo'l kerak.

Keyingi dars: OOP tamoyillari va kompozitsiya — OOP'ning to'rt ustunini ko'ramiz va nega ko'pincha meros o'rniga kompozitsiya tanlanishini vazifalar da amalda ko'rsatamiz.

Manbalar

  • MDN: "extends", "super", "Classes — Inheritance" — developer.mozilla.org
  • MDN: "Error — Custom error types", "EventTarget() constructor" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "ClassDefinitionEvaluation", "SuperCall" — tc39.es/ecma262
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript klass merosi: extends va super bilan ota klass ustiga qurish — IlmHamroh