IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur36/65-dars22 daqiqa
Mundarija (37)

JavaScript class sintaksisi: konstruktor, metodlar va new

Qisqacha: Klass (class) — bir xil tuzilishdagi obyektlar uchun qolip: class Taom { constructor(nom) { this.nom = nom; } tavsifla() { ... } }, nusxa esa new Taom("Osh") bilan yaratiladi. Klass — prototip ustidagi qulay sintaksis: constructor konstruktor funksiyaning o'rnini, metodlar esa Taom.prototype dagi funksiyalarning o'rnini egallaydi. Farqi — klassni new siz chaqirib bo'lmaydi va uni e'londan oldin ishlatib bo'lmaydi.

Bu darsda

  • class, constructor va metodlar bilan klass yozib, new bilan nusxalar yarata olasiz.
  • Klass ichida aslida qaysi prototip zanjiri qurilishini tushuntira olasiz.
  • Klass va oddiy konstruktor funksiyaning farqlarini — new majburiyligi, TDZ, metodlar sanalmasligi — bilasiz.
  • Class expression yozasiz va klassni qiymat sifatida funksiyaga uzata olasiz.
  • vazifalar dagi har vazifani Vazifa klassiga o'tkazasiz — xulq o'zgarmaydi.

Oldin bilishingiz kerak: Konstruktor funksiya va new, Object.create va instanceof, this: to'rt qoida, Temporal Dead Zone.

1. Nega bu kerak?

Konstruktor funksiya darsida taomlar uchun "qolip" yozgan edik. Eslaymiz:

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

Taom.prototype.tavsifla = function () {
  return `${this.nom}: ${this.narx} so'm`;
};

Taom.prototype.chegirmaBer = function (foiz) {
  this.narx = this.narx - (this.narx * foiz) / 100;
};

const osh = new Taom("Osh", 35000);
console.log(osh.tavsifla()); // Osh: 35000 so'm

Kod ishlaydi, lekin uchta noqulayligi bor.

Birinchisi — qolip tarqoq: konstruktor bir joyda, metodlar pastda alohida tayinlashlar bilan. Metod ko'paygan sari ular orasiga boshqa kod kirib qolishi mumkin. Ikkinchisi — Taom oddiy funksiyadek ko'rinadi: Taom("Osh") deb new siz chaqirsangiz, xato chiqmaydi, faqat narsa noto'g'ri joyga yoziladi. Uchinchisi — har metod oldida Taom.prototype. ni qayta-qayta yozish.

Klass shu uchala muammoni hal qiladi. Konstruktor ham, metodlar ham bitta blok ichida turadi; new siz chaqirish darhol xato beradi. Ichida esa o'sha prototip mexanizmi ishlaydi — yangi narsa o'ylab topilmagan. Shuning uchun klassni ko'pincha "prototip ustidagi qulay sintaksis" deyishadi. Bu iborani class ichidan darsida chuqur ochamiz; bugun klassni yozish va ishlatishni o'rganamiz.

Hayotdan o'xshatish: g'isht quyish qolipi. Qolipning o'zi g'isht emas — u g'ishtning shaklini belgilaydi. Har quyishda yangi g'isht chiqadi, hammasi bir xil shaklda, lekin har biri alohida. Klass — qolip, new — quyish, nusxa — g'isht.

09-qismda Custom events darsida class Savat extends EventTarget kodini "shaklini tanib qoling" deb ko'rsatgan edik. Bugundan boshlab bunday kodni o'qiy boshlaysiz.

2. Birinchi klass

2.1 Konstruktor funksiyadan klassga

Xuddi o'sha Taom, klass bilan:

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

  tavsifla() {
    return `${this.nom}: ${this.narx} so'm`;
  }

  chegirmaBer(foiz) {
    this.narx = this.narx - (this.narx * foiz) / 100;
  }
}

const osh = new Taom("Osh", 35000);
osh.chegirmaBer(10);

console.log(osh.tavsifla()); // Osh: 31500 so'm
console.log(osh); // Taom { nom: 'Osh', narx: 31500 }

Qatorma-qator:

  • class Taom { ... } — klass e'loni. Nomi bosh harf bilan, xuddi konstruktor funksiyadek — bu "new bilan chaqiriladi" degan kelishuv.
  • constructor(nom, narx) { ... } — nusxa yaratilganda ishlaydigan maxsus metod. Uning tanasi — eski function Taom ning tanasi: this — yangi obyekt, unga xususiyatlar yoziladi.
  • tavsifla() { ... } — metod. function so'zi yo'q, metodlar orasida vergul yo'q — obyekt literalidagi qisqa metod yozuviga o'xshaydi (Shorthand va metodlar), faqat vergulsiz.
  • new Taom("Osh", 35000) — nusxa yaratish. Argumentlar constructor ga boradi.

Nomlash kelishuvi kursdagi funksiya nomlari bilan izchil: klass nomi — ot, birlikda va bosh harf bilan (Taom, Buyurtma, Vazifa), chunki u "nima" ekanini aytadi. Metod nomi esa — fe'l yoki natija nomi (tavsifla, chegirmaBer), chunki u "nima qiladi" degan savolga javob. true/false qaytaradigan metod — "-mi" bilan (bandmi).

Bu kelishuv ko'p tilda bir xil: Java, C#, Python'da ham klass nomi bosh harf bilan yoziladi. Shuning uchun new taom() yoki class taomlar ko'rsangiz, kod ishlasa ham, jamoadoshingiz uni tuzatishni so'raydi.

Nusxa (instance) — klassdan new bilan yaratilgan obyekt. osh — Taom ning nusxasi. Node uni Taom { nom: 'Osh', narx: 31500 } deb, klass nomi bilan ko'rsatadi.

2.2 Klass ichidan: o'sha prototip

Klass yangi tur emas. Tekshiramiz:

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

  tavsifla() {
    return `Bugun: ${this.nom}`;
  }
}

const osh = new Taom("Osh");

console.log(typeof Taom); // function
console.log(Object.getPrototypeOf(osh) === Taom.prototype); // true
console.log(Object.hasOwn(osh, "tavsifla")); // false
console.log(Object.hasOwn(Taom.prototype, "tavsifla")); // true
console.log(osh instanceof Taom); // true

Hammasi Konstruktor funksiya darsidagidek:

  • typeof Taom — "function". Klass — maxsus turdagi funksiya.
  • osh ning prototipi — Taom.prototype.
  • Metod nusxada emas, Taom.prototype da. Ming nusxa bo'lsa ham tavsifla bitta.
  • instanceof Object.create va instanceof darsidagidek ishlaydi.

Diagrammada:

flowchart LR
  K["class Taom"] -- "prototype" --> P["Taom.prototype<br/>constructor, tavsifla"]
  O1["osh<br/>nom: Osh"] --> P
  O2["manti<br/>nom: Manti"] --> P
  P --> OP["Object.prototype"]

Uzluksiz o'qlar — nusxalarning prototip havolasi, prototype yozuvli o'q esa klassning xususiyati. Rasm konstruktor funksiyadagi bilan bir xil: klass faqat yozuvni o'zgartirdi.

Tekshirib ko'ring: Nusxalar yaratilgandan keyin Taom.prototype.yangiMetod = function () { return "yangi"; } qo'shilsa, osh.yangiMetod() ishlaydimi?

Javob

Ishlaydi va "yangi" qaytaradi. Prototip — jonli havola (Object.create darsidagidek): osh metodni har safar Taom.prototype dan qidiradi, u yerda esa endi yangiMetod bor. Lekin bunday "keyin qo'shish" real kodda yozilmaydi — metodlar klass tanasida tursin, aks holda klassni o'qigan odam to'liq rasmni ko'rmaydi.

2.3 new qadamma-qadam

new Taom("Osh") ichida new ning to'rt qadami bajariladi. Keyingi tugmasi bilan kuzating:

O'zingiz to'ldiring. class Ichimlik { constructor(nom) { this.nom = nom; } } dan keyin typeof Ichimlik — [:function], new Ichimlik("Choy").nom — [:Choy].

Tekshirib ko'ring: osh.tavsifla === manti.tavsifla (ikkalasi Taom nusxasi) nima qaytaradi? Nega?

Javob

true. Ikkala nusxada ham tavsifla yo'q — ikkalasi uni bitta joydan, Taom.prototype dan oladi. Bu bitta funksiya. Agar metodni constructor ichida this.tavsifla = function () {...} deb yozsangiz, javob false bo'lardi: har nusxaga alohida funksiya yaratilardi.

3. constructor haqida batafsil

3.1 U ixtiyoriy

Nusxaga boshlang'ich ma'lumot kerak bo'lmasa, constructor yozilmaydi. JavaScript bo'sh konstruktorni o'zi qo'shadi:

js
class Kassa {
  salomBer() {
    return "Xush kelibsiz, «Bahor»ga!";
  }
}

const kassa = new Kassa();
console.log(kassa.salomBer()); // Xush kelibsiz, «Bahor»ga!
console.log(kassa); // Kassa {}

3.2 Faqat bitta

Bitta klassda ikkita constructor bo'lmaydi — SyntaxError: A class may only have one constructor ("klassda faqat bitta konstruktor bo'lishi mumkin"). Har xil argumentlar bilan yaratish kerak bo'lsa — default parametrlar yordam beradi:

js
class Buyurtma {
  constructor(taom, soni = 1, stol = null) {
    this.taom = taom;
    this.soni = soni;
    this.stol = stol;
  }
}

console.log(new Buyurtma("Osh"));
console.log(new Buyurtma("Ko'k choy", 2, 5));

Konsolda:

text
Buyurtma { taom: 'Osh', soni: 1, stol: null }
Buyurtma { taom: "Ko'k choy", soni: 2, stol: 5 }

Parametr ko'p bo'lsa, options obyekti va destructuring bilan yozish o'qishga qulayroq: constructor({ taom, soni = 1, stol = null }).

Tekshirib ko'ring: new Buyurtma("Manti", undefined, 3) qanday obyekt yaratadi?

Javob

Buyurtma { taom: 'Manti', soni: 1, stol: 3 }. Default qiymat argument undefined bo'lganda ishlaydi — yozilmagan bo'lsa ham, ataylab undefined berilgan bo'lsa ham (Parametrlar va default qiymatlar). null berilsa esa default ishlamaydi — soni: null bo'lardi.

3.3 return yozmang

Konstruktor odatda hech narsa qaytarmaydi — new yangi obyektni o'zi qaytaradi. Konstruktorda return { ... } yozilsa, new yangi nusxa o'rniga o'sha obyektni qaytaradi va u klass nusxasi bo'lmaydi. Ko'ring:

js
class Taom {
  constructor(nom) {
    this.nom = nom;
    return { nom: "Boshqa" }; // ❌ shunday yozmang
  }

  tavsifla() {
    return this.nom;
  }
}

const osh = new Taom("Osh");
console.log(osh); // { nom: 'Boshqa' }
console.log(osh instanceof Taom); // false
console.log(typeof osh.tavsifla); // undefined

osh — Taom emas, oddiy obyekt: metodi ham yo'q. Bunday kod chalkash — uni yozmang. Bu qoida konstruktor funksiyada ham xuddi shunday edi.

4. Metodlar va this

4.1 Metod ichida this

Metod ichidagi this — metod chaqirilgan nusxa (this: to'rt qoida). Bitta metod boshqasini ham this orqali chaqiradi:

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

  qqsBilan() {
    return this.narx * 1.12;
  }

  chekQatori() {
    return `${this.nom}: ${this.qqsBilan()} so'm, QQS bilan`;
  }
}

const manti = new Taom("Manti", 30000);
console.log(manti.chekQatori()); // Manti: 33600 so'm, QQS bilan

Bu yerda metodlar bir-biriga tayanib ishlaydi: chekQatori narxni o'zi hisoblamaydi, qqsBilan dan so'raydi. Ertaga QQS stavkasi o'zgarsa, bitta metodni tuzatasiz — chek qatori o'zi yangisini oladi. Ma'lumot (nom, narx) va unga tegishli amallar bitta qolipda yashashining asosiy foydasi shu.

chekQatori ichida qqsBilan() emas, this.qqsBilan(). Metod nusxaning o'zida emas, prototipda — unga faqat obyekt orqali yetib boriladi. this. siz yozsangiz, JavaScript oddiy o'zgaruvchi qidiradi va ReferenceError: qqsBilan is not defined beradi.

4.2 Metodni ajratib olsa: this yo'qoladi

this yo'qolishi darsidagi tuzoq klassda ham bor — va ancha aniq ko'rinadi:

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

  tavsifla() {
    return `Bugun: ${this.nom}`;
  }
}

const osh = new Taom("Osh");
const tavsif = osh.tavsifla;
console.log(tavsif());
text
TypeError: Cannot read properties of undefined (reading 'nom')

Tarjimasi: "undefined ning nom xususiyatini o'qib bo'lmaydi". tavsif() — obyektsiz chaqiruv, this — undefined. Klass tanasi doim strict mode da ishlaydi, shuning uchun this hech qachon globalThis ga aylanmaydi — xato darhol chiqadi. Oddiy funksiyada esa jimgina noto'g'ri natija bo'lishi mumkin edi.

Yechimlar o'sha darsdagidek: o'ram (() => osh.tavsifla()) yoki osh.tavsifla.bind(osh) (call, apply, bind). Klassning o'zida ham bitta yo'l bor — uni keyingi darsda, Class maydonlari da ko'ramiz.

Tekshirib ko'ring: [osh, manti].map((t) => t.tavsifla()) ishlaydimi? [osh, manti].map(Taom.prototype.tavsifla) chi?

Javob

Birinchisi ishlaydi: har chaqiruv t.tavsifla() shaklida, this — t. Ikkinchisi — TypeError: map metodni obyektsiz chaqiradi, this — undefined. Callback imzosi darsidagi thisArg bilan (map(fn, osh)) ham chiqmaydi — hamma elementda this bitta osh bo'lib qoladi.

5. Klass va konstruktor funksiya: farqlari

Klass "shakar" bo'lsa ham, uning bir nechta qat'iy qoidasi bor. To'rttasini hozir bilish kerak, qolganlari — class ichidan darsida.

5.1 new majburiy

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

const osh = Taom("Osh");
text
TypeError: Class constructor Taom cannot be invoked without 'new'

Tarjimasi: "Taom klass konstruktorini new siz chaqirib bo'lmaydi". Konstruktor funksiya bu holatda jim qolardi — klass esa xatoni darhol ko'rsatadi.

5.2 E'londan oldin ishlatib bo'lmaydi

function e'loni "ko'tariladi" — uni fayl boshida ham chaqirsa bo'ladi (Hoisting). Klass esa let va const kabi: e'lon qatorigacha TDZ da turadi.

js
const osh = new Taom("Osh");

class Taom {
  constructor(nom) {
    this.nom = nom;
  }
}
text
ReferenceError: Cannot access 'Taom' before initialization

Tarjimasi: "Taom ga ishga tushirilishidan oldin murojaat qilib bo'lmaydi". Klass bor — lekin hali tayyor emas. Qoida: klassni ishlatadigan koddan yuqorida e'lon qiling. Bu darsning vazifalar qadamida aynan shu qoida kerak bo'ladi.

5.3 Tana — doim strict

Klass ichidagi hamma kod strict mode da ishlaydi, faylning boshida "use strict" bo'lmasa ham. Oddiy <script> da ham. «Metodni ajratib olsa» bo'limidagi aniq TypeError — shuning natijasi.

5.4 Metodlar sanalmaydi

Konstruktor funksiyada Taom.prototype.tavsifla = ... oddiy tayinlash edi — metod sanaladigan bo'lardi va for...in da chiqardi. Klass metodlari esa sanalmaydi:

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

  tavsifla() {
    return this.nom;
  }
}

for (const kalit in new Taom("Osh")) {
  console.log(kalit); // nom
}

for...in faqat nom ni berdi — metod o'rnatilgan metodlar kabi "yashirin". Xuddi polyfill uchun defineProperty bilan qilganimizdek, klass buni o'zi qiladi.

Jadvalda:

Xususiyat function Taom class Taom
new siz chaqirish jim, noto'g'ri natija TypeError
E'londan oldin ishlatish mumkin (hoisting) ReferenceError (TDZ)
Strict mode faylga bog'liq doim
Metodlar for...in da ko'rinadi ko'rinmaydi

Tekshirib ko'ring: Nega klass nomini new siz chaqirish xatosi foydali?

Javob

Konstruktor funksiyani new siz chaqirsangiz, this noto'g'ri joyni ko'rsatadi (oddiy rejimda — global obyektni) va this.nom = nom global o'zgaruvchi yaratib yuboradi. Natija — undefined va qayerdadir "sirli" global. Xato bir necha qadam keyin, boshqa joyda ko'rinadi. Klass esa xatoni o'sha qatorning o'zida, aniq xabar bilan beradi.

6. Class expression va klass — qiymat

6.1 Klassni o'zgaruvchiga yozish

Funksiyadagidek, klassni ifoda sifatida ham yozish mumkin (Function expression):

js
const Ichimlik = class {
  constructor(nom) {
    this.nom = nom;
  }
};

const choy = new Ichimlik("Ko'k choy");
console.log(choy); // Ichimlik { nom: "Ko'k choy" }
console.log(Ichimlik.name); // Ichimlik

Class expression — o'zgaruvchiga yoki argumentga beriladigan klass. Nomni JavaScript o'zgaruvchidan oladi. Amalda klass e'loni (class Ichimlik {}) ko'proq uchraydi; ifoda shakli — klassni boshqa joyga uzatish kerak bo'lganda.

6.2 Klassni funksiyaga uzatish

Klass — funksiya, funksiya esa qiymat (Callback va HOF). Demak klassni argument qilib berish mumkin:

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

class Ichimlik {
  constructor(nom) {
    this.nom = nom;
  }
}

function hammasiniYarat(Klass, nomlar) {
  return nomlar.map((nom) => new Klass(nom));
}

console.log(hammasiniYarat(Taom, ["Osh", "Manti"]));
console.log(hammasiniYarat(Ichimlik, ["Ko'k choy"]));

Konsolda:

text
[ Taom { nom: 'Osh' }, Taom { nom: 'Manti' } ]
[ Ichimlik { nom: "Ko'k choy" } ]

hammasiniYarat qaysi klass kelishini bilmaydi — u faqat "new bilan chaqiraman" deydi. Parametr nomi bosh harf bilan (Klass) — "bu yerga klass keladi" degan ishora.

Tekshirib ko'ring: hammasiniYarat(Taom, []) nima qaytaradi? Klass bir marta ham chaqiriladimi?

Javob

Bo'sh massiv []. map bo'sh massivda callback'ni umuman chaqirmaydi (map) — demak new Klass(...) ham bir marta ham bajarilmaydi. Natija — xatosiz bo'sh ro'yxat, aynan kutilganidek.

6.3 Qachon klass, qachon oddiy obyekt?

Klassni o'rgangan zahoti hamma narsani klass qilib yozish istagi paydo bo'ladi. Shoshilmang. Uchta savol yordam beradi:

  1. Bir xil tuzilishdagi obyekt ko'pmi? Vazifalar, taomlar, buyurtmalar — yuzlab nusxa, hammasida bir xil xususiyatlar. Ha — klass foydali. «Bahor» sozlamalari esa bitta obyekt; uni klass qilish ortiqcha — oddiy literal yetadi.
  2. Ma'lumot bilan birga xulq ham bormi? almashtir(), chegirmaBer() kabi metodlar ma'lumotning o'zini o'zgartirsa — ular bitta joyda tursin. Faqat ma'lumot bo'lsa (masalan, serverdan kelgan javob) — oddiy obyekt qulayroq: uni JSON'ga aylantirish ham, nusxalash ham oson.
  3. Funksiya yetmaydimi? Ichida holat saqlamaydigan hisob-kitob — narxniHisobla(taom, soni) — oddiy funksiya bo'lib qolsin. Uni klass metodiga aylantirish faqat chaqirishni qiyinlashtiradi.

JavaScript'da klass — vositalardan biri, majburiy uslub emas. Tajribali jamoalar ham funksiyalar, oddiy obyektlar va klasslarni aralash ishlatadi. Har birining o'rnini OOP tamoyillari va kompozitsiya darsida chuqurroq muhokama qilamiz.

7. new bilan tanish o'rnatilgan klasslar

Endi siz ilgari ko'rgan new lar ham tushunarli: new Date(), new Map() (Map — keyinroq), new DocumentFragment(), new CustomEvent("ozgardi"). Bularning hammasi — brauzer yoki JavaScript tayyorlab qo'ygan klasslar (yoki xuddi klassdek ishlaydigan konstruktorlar). new ularning nusxasini yaratadi, metodlar esa ularning prototipida yashaydi.

Teskari tomoni ham bor: O'rnatilgan prototiplar darsida BigInt va Symbol ni new bilan chaqirib bo'lmasligini ko'rdik — ular klass emas, primitiv qaytaradigan funksiyalar.

09-qismdagi Savat ga qaytaylik:

js
class Savat extends EventTarget {
  taomlar = [];

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

Endi uning katta qismini o'qiy olasiz: class Savat, qosh metodi, this.taomlar, new CustomEvent. Ikkita yangi narsa qoldi. taomlar = [] — konstruktorsiz e'lon qilingan xususiyat, uni keyingi darsda o'rganamiz. extends EventTarget — meros, u Meros: extends va super darsida.

8. Ko'p uchraydigan xatolar

8.1 Metodlar orasida vergul

js
class Taom {
  tavsifla() {
    return "osh";
  },
  narxla() {
    return 35000;
  }
}
text
SyntaxError: Unexpected token ','

Tarjimasi: "kutilmagan belgi: ,". Obyekt literalidagi odat — klass ichida vergul yo'q. Tuzatish: vergulni o'chiring.

8.2 Metod oldida function

js
class Taom {
  function tavsifla() {
    return "osh";
  }
}
text
SyntaxError: Unexpected identifier 'tavsifla'

Tarjimasi: "kutilmagan nom: tavsifla". JavaScript function ni metod nomi deb o'qidi, undan keyin esa ( kutgan edi — tavsifla ni emas. Xabar xato bo'lmagan so'zni ko'rsatadi: oldingi so'zni tekshiring. Tuzatish: function ni o'chiring.

8.3 this. siz xususiyat

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

  tavsifla() {
    return `Bugun: ${nom}`;
  }
}

console.log(new Taom("Osh").tavsifla());
text
ReferenceError: nom is not defined

Tarjimasi: "nom aniqlanmagan". nom parametri faqat constructor ichida yashaydi. Boshqa metodda u yo'q — u yerda faqat this.nom bor. Tuzatish: ${this.nom}.

8.4 new ni unutish, klassni pastda e'lon qilish

«Farqlari» bo'limidagi ikki xato: Class constructor Taom cannot be invoked without 'new' va Cannot access 'Taom' before initialization. Tuzatish: new Taom(...) va klass e'lonini fayl tepasiga ko'chirish.

8.5 Metodni constructor ichida yaratish

Konstruktor funksiyadan ko'chirayotganda ba'zan shunday kod chiqadi:

js
class Taom {
  constructor(nom) {
    this.nom = nom;
    this.tavsifla = function () { // ❌ har nusxaga yangi funksiya
      return this.nom;
    };
  }
}

Kod ishlaydi, lekin metod endi prototipda emas — har nusxaning o'zida. Ming taom — ming bir xil funksiya xotirada. console.log ham nusxani Taom { nom: 'Osh', tavsifla: [Function (anonymous)] } deb ifloslab ko'rsatadi va JSON.stringify uni tashlab yuboradi. Tuzatish: metodni klass tanasiga, constructor dan tashqariga chiqaring. Konstruktor faqat ma'lumot (xususiyatlar) bilan shug'ullansin, xulq — metodlarda.

9. Mashqlar

1-mashq (oson): Ichimlik klassi

Ichimlik klassini yozing: constructor(nom, narx), tavsifla() metodi "Ko'k choy: 5000 so'm" ko'rinishida satr qaytarsin. Ikki nusxa yaratib, tavsifini chiqaring.

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

  tavsifla() {
    return `${this.nom}: ${this.narx} so'm`;
  }
}

const choy = new Ichimlik("Ko'k choy", 5000);
const ayron = new Ichimlik("Ayron", 7000);

console.log(choy.tavsifla()); // Ko'k choy: 5000 so'm
console.log(ayron.tavsifla()); // Ayron: 7000 so'm

Ayron narxi — misol uchun; «Bahor» kanonik menyusida faqat ko'k choy narxi bor.

2-mashq (o'rta): Konstruktor funksiyadan klassga

Quyidagi kodni klassga o'tkazing. Natija o'zgarmasin.

js
function Stol(raqam, joylar) {
  this.raqam = raqam;
  this.joylar = joylar;
  this.band = false;
}
Stol.prototype.bandQil = function () {
  this.band = true;
};
Stol.prototype.sigadimi = function (odam) {
  return !this.band && odam <= this.joylar;
};

Ishora: constructor ga funksiya tanasi, metodlarga Stol.prototype. siz nomlar. Predikat nomi "-mi" bilan qoladi.

Yechim
js
class Stol {
  constructor(raqam, joylar) {
    this.raqam = raqam;
    this.joylar = joylar;
    this.band = false;
  }

  bandQil() {
    this.band = true;
  }

  sigadimi(odam) {
    return !this.band && odam <= this.joylar;
  }
}

const stol = new Stol(5, 4);
console.log(stol.sigadimi(3)); // true
stol.bandQil();
console.log(stol.sigadimi(3)); // false

Uchta tarqoq bo'lak bitta blokka yig'ildi. Ichida — o'sha Stol.prototype va o'sha ikki metod.

3-mashq (qiyin): Klassni tekshirish

Taom klassi uchun quyidagilarni kod bilan isbotlang: (1) tavsifla nusxada emas, prototipda; (2) metod sanalmaydi; (3) Taom.prototype.constructor === Taom. Ishora: Object.hasOwn, Object.keys, Object.getOwnPropertyNames (Xususiyat deskriptorlari).

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

  tavsifla() {
    return this.nom;
  }
}

const osh = new Taom("Osh");
const proto = Taom.prototype;

console.log(Object.hasOwn(osh, "tavsifla")); // false
console.log(Object.hasOwn(proto, "tavsifla")); // true
console.log(Object.keys(proto)); // []
console.log(Object.getOwnPropertyNames(proto));
console.log(proto.constructor === Taom); // true

Konsolda to'rtinchi qator: [ 'constructor', 'tavsifla' ]. Object.keys bo'sh — ikkalasi ham sanalmaydi; getOwnPropertyNames esa sanalmaydiganlarni ham beradi. constructor xususiyati — xuddi konstruktor funksiyadagidek, klassga qaytuvchi havola.

4-mashq: Vazifalar qadami — Vazifa klassi

vazifalar ga qaytamiz. Hozir har vazifa — oddiy obyekt. Boshlang'ich uchtasi muzlatilgan boshlangichVazifalar da turadi (Obyektni himoyalash), yangisi vazifaQosh da literal bilan yasaladi. O'zgartirish esa vazifadan tashqarida, spread bilan (Immutable yangilash):

js
function vazifaQosh(matn) {
  holat.vazifalar = [
    ...holat.vazifalar,
    { id: yangiId(), matn, bajarildi: false },
  ];
  // ...
}

function vazifaniAlmashtir(id) {
  const bajarildi = !vazifaniTop(id).bajarildi;
  vazifaniYangila(id, { bajarildi });
  render();
}

Vazifa qolipi — id, matn, bajarildi — bir necha joyda takrorlangan, "belgilash" esa vazifadan tashqarida. Bu — klass uchun aniq nomzod. Object.create darsidagi mashq aynan shunga tayyorgarlik edi.

Yangi branch: git switch -c refactor/vazifa-klassi. refactor/ — xulq o'zgarmaydi. Bu va keyingi ikki dars shu branch'da davom etadi.

  1. Vazifa klassini yozing: constructor(id, matn, bajarildi = false) va almashtir() metodi.
  2. holat.vazifalar ni boshlangichVazifalar dan new Vazifa(...) bilan yasang. boshlangichVazifalar ning o'zi muzlatilgan oddiy ma'lumot bo'lib qolsin.
  3. vazifaQosh, vazifaniAlmashtir va matnniAlmashtir ni klassdan foydalanadigan qiling.
  4. Klassni faylning qayeriga qo'yish kerakligini «E'londan oldin ishlatib bo'lmaydi» bo'limiga qarab hal qiling.

Ishora: vazifaniYangila ichidagi spread ({ ...vazifa, ...ozgarish }) Vazifa nusxasidan qanday obyekt yasaydi? Avval Node'da sinab ko'ring.

Yechim

Avval ishora. Spread obyektning faqat o'z xususiyatlarini ko'chiradi, prototipini emas (Spread va rest):

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

  almashtir() {
    this.bajarildi = !this.bajarildi;
  }
}

const non = new Vazifa(1, "Non olish");
const nusxa = { ...non, bajarildi: true };

console.log(nusxa); // { id: 1, matn: 'Non olish', bajarildi: true }
console.log(nusxa instanceof Vazifa); // false
console.log(typeof nusxa.almashtir); // undefined

nusxa — oddiy obyekt: unda na Vazifa nomi, na almashtir metodi bor. Metod Vazifa.prototype da edi, spread esa prototipga qaramaydi. vazifaniYangila ilovada qolsa, birinchi belgilashdan keyin ro'yxatda Vazifa nusxalari va oddiy obyektlar aralashib ketardi.

Qoida: klass nusxasi spread bilan nusxalanmaydi — uni o'z metodi o'zgartiradi. O'zgarish klass ichida, nazorat ostida bo'ladi — buni Private # maydon darsida mustahkamlaymiz. Immutable yangilash usuli esa yo'qolmaydi: u oddiy ma'lumot uchun qoladi — muzlatilgan boshlangichVazifalar va eksport paketi shunday.

Klass — vazifalar.js ning eng boshiga, "1. Holat" bo'limidan oldin:

js
// 0. Vazifa klassi — har vazifa shu qolipdan yasaladi
class Vazifa {
  constructor(id, matn, bajarildi = false) {
    this.id = id;
    this.matn = matn;
    this.bajarildi = bajarildi;
  }

  almashtir() {
    this.bajarildi = !this.bajarildi;
  }
}

Holat va uch funksiya:

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

function vazifaQosh(matn) {
  holat.vazifalar = [
    ...holat.vazifalar,
    new Vazifa(yangiId(), matn),
  ];
  if (holat.filtr === "bajarilgan") {
    holat.filtr = "hammasi";
  }
  render();
}

function vazifaniAlmashtir(id) {
  vazifaniTop(id).almashtir();
  render();
}

function matnniAlmashtir(id, matn) {
  vazifaniTop(id).matn = matn;
  holat.tahrirId = null;
  render();
}

vazifaniYangila ni endi hech kim chaqirmaydi — uni o'chiring. vazifaniOchir (filter) o'zgarmaydi: u massivni yangisiga almashtiradi, vazifalarning o'ziga tegmaydi.

Nima o'zgardi va nima o'zgarmadi:

  • Joylashuv muhim. Klassni holat dan pastga qo'ysangiz, sahifa ochilishi bilan konsolda Uncaught ReferenceError: Cannot access 'Vazifa' before initialization chiqadi va ro'yxat bo'sh qoladi — holat klass tayyor bo'lmasdan uni chaqiradi (TDZ).
  • boshlangichVazifalar ga tegilmadi. map undan yangi Vazifa nusxalarini yasaydi. Belgilash nusxani o'zgartiradi, muzlatilgan ma'lumot esa butun qoladi: Console'da boshlangichVazifalar[0].bajarildi — hamon false.
  • bajarildi = false — default parametr. Yangi vazifa doim bajarilmagan, faqat boshlang'ich ma'lumotda true bo'lishi mumkin.
  • Qolgan kod tegilmadi. vazifa.id, vazifa.matn, vazifa.bajarildi ni o'qiydigan hamma joy — mosmi, qatorYasa, statistika, xatoniTop — avvalgidek ishlaydi: nusxada ham o'sha uch xususiyat bor.
  • Eksport natijasi ham o'zgarmadi — metod prototipda, JSON.stringify esa faqat o'z xususiyatlarni oladi (JSON asoslari).

Xulq o'zgarmaganini tekshiring (Live Server):

  • Ochilganda — uch vazifa, "2 ta vazifa qoldi, 1 ta bajarildi", konsolda xato yo'q.
  • «Faol» da ikkala vazifani Space bilan belgilang — "Faol vazifa yo'q.", fokus "Ro'yxat" sarlavhasida.
  • Yangi vazifa qo'shing, uni belgilang va qaytaring — hisob to'g'ri o'zgaradi. Uni tahrirlab Enter bosing — matn yangilanadi.
  • Esc, o'chirish, takror matn xatosi va eksport — avvalgidek.

Commit: Vazifalar: vazifani Vazifa klassiga o'tkaz. Branch'ni hali main ga qo'shmang.

10. Real ishda

  • Brauzer va Node API'lari — deyarli hammasi klass: Map, Date, URL, Error, EventTarget, DOM elementlari. Ularning hujjatini o'qiganda "constructor", "instance methods" bo'limlari — aynan bugungi tushunchalar.
  • Freymvorklar. Angular va NestJS (30-qismda) kodning asosiy qismini klasslarda yozadi. React'da hozir funksiya komponentlar asosiy, lekin eski loyihalarda class App extends React.Component ni ko'p uchratasiz.
  • Domen modellari. Real ilovalarda biznes tushunchalari — Buyurtma, Foydalanuvchi, Tolov — ko'pincha klass bo'ladi: ma'lumot va qoidalar (masalan, "to'langan buyurtmani bekor qilib bo'lmaydi") bir joyda. Bu yondashuv 30-qismdagi NestJS va backend arxitekturasida asosiy uslub.
  • TypeScript (15-qism) klasslarga turlar va kirish modifikatorlarini qo'shadi — bugungi sintaksis uning poydevori.
  • Intervyu. "Klass va konstruktor funksiya farqi?", "Klass hoisting bo'ladimi?", "Metodlar qayerda saqlanadi?" — hammasining javobi shu darsda. Ko'p intervyuchi qo'shimcha so'raydi: "Klassni konstruktor funksiyaga qo'lda o'girib bering". Endi bu ikki yo'nalishda ham qo'lingizdan keladi — «Konstruktor funksiyadan klassga» bo'limi va 2-mashq aynan shu mashq edi.

Xulosa

  • Klass — bir xil tuzilishdagi obyektlar qolipi: constructor + metodlar, nusxa new bilan.
  • Ichida o'sha prototip: typeof Klass — "function", metodlar Klass.prototype da, nusxada faqat this. bilan yozilganlar.
  • Metodlar orasida vergul va function so'zi yo'q; boshqa metod va xususiyatga this. orqali murojaat qilinadi.
  • Klassni new siz chaqirib bo'lmaydi, e'londan oldin ishlatib bo'lmaydi (TDZ), tanasi doim strict, metodlari sanalmaydi.
  • Klass — qiymat: uni o'zgaruvchiga yozish va funksiyaga uzatish mumkin.

Keyingi dars: Class maydonlari — xususiyatlarni konstruktorsiz, klass tanasida e'lon qilishni va this yo'qolishini arrow maydon bilan hal qilishni (va uning narxini) o'rganamiz.

Manbalar

  • MDN: "Classes", "constructor", "class expression" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "Class Definitions" — tc39.es/ecma262
  • web-features: class-syntax — Baseline 2016-03 (keng qo'llab-quvvatlanadi 2018-09 dan)
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript class sintaksisi: konstruktor, metodlar va new — IlmHamroh