Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. Ichkarida — o'sha prototip
- 2.1 Bitta klass, ikki yozuv
- 2.2 Getter ham — eski vosita bilan
- 2.3 extends ham — o'sha zanjir
- 2.4 DevTools'da ko'ring
- 3. Shakardan ko'proq: besh farq
- 3.1 new siz chaqirib bo'lmaydi
- 3.2 Ichi doim strict
- 3.3 Metodlar sanalmaydi
- 3.4 Hoisting: TDZ
- 3.5 super — [[HomeObject]] ga bog'langan
- 3.6 Yana ikki kichik himoya
- 4. Maydonlar va #private: haqiqiy yangilik
- 4.1 Maydon setter'ni chaqirmaydi
- 4.2 #private — shakar emas
- 4.3 Farqlar bitta jadvalda
- 5. Ko'p uchraydigan xatolar
- 5.1 new ni unutish
- 5.2 Klass nusxasini nusxalash yoki JSON'ga aylantirish
- 5.3 Klass ichidagi metodni callback qilib berish
- 5.4 Ota setter'i bilan bir xil nomli maydon
- 5.5 "Klass — boshqa tillardagidek" deb o'ylash
- 6. Mashqlar
- 1-mashq (oson): Klassni "ochib" yozing
- 2-mashq (o'rta): Nima chiqadi?
- 3-mashq (qiyin): super ni qo'lda
- 4-mashq: Vazifalar qadami — royxatni JSON'ga qaytarish
- 7. Real ishda
- Xulosa
- Manbalar
JavaScript class ichidan: prototip ustidagi "shakar" va uning muhim farqlari
Qisqacha:
class— konstruktor funksiya vaprototypening qulay yozuvi: ichkarida o'sha funksiya, o'sha prototip zanjiri. Lekin u faqat "shakar" emas, bir necha himoyani ham qo'shadi. Klassninewsiz chaqirib bo'lmaydi, ichi doim strict, metodlarifor...inda ko'rinmaydi.superesa metod yozilgan klassga bog'langan.#privatemaydonlar umuman yangi imkoniyat — ularni eski sintaksisda yozib bo'lmaydi.
Bu darsda
classni konstruktor funksiya va prototipga "qaytarib" yoza olasiz va ikkalasining tuzilmasi bir xilligini tekshira olasiz.- Klass va konstruktor funksiya orasidagi besh muhim farqni ayta olasiz.
- Maydon va konstruktordagi tayinlash nega har xil ishlashini tushuntira olasiz.
superqanday qilib "o'z otasini" topishini —[[HomeObject]]ni — bilasiz.vazifalardagiVazifalarRoyxatini JSON'ga to'g'ri aylanadigan qilasiz.
Oldin bilishingiz kerak: Konstruktor funksiya va new, Strict mode, Xususiyat deskriptorlari, OOP tamoyillari va kompozitsiya.
1. Nega bu kerak?
Internetda, kitoblarda va intervyularda bitta gapni ko'p eshitasiz: "JavaScript'dagi class — shunchaki sintaktik shakar". Bu gap yarim rost. Yarmi rostligini bilmasangiz, ikki xil xatoga tushasiz.
Birinchisi: "klass — boshqa tillardagidek alohida narsa" deb o'ylash. Unda typeof Taom nega "function" ekani, metod nega nusxada emas, prototipda turgani sir bo'lib qoladi. DevTools'da [[Prototype]] ni ko'rib adashasiz.
Ikkinchisi: "klass — konstruktor funksiyaning o'zi" deb o'ylash. Unda eski kodni klassga ko'chirganingizda kutilmagan TypeError lar chiqadi — chunki klass bir necha joyda qattiqroq.
Sintaktik shakar (syntactic sugar) — tilga yangi imkoniyat qo'shmaydigan, faqat mavjud narsani qulayroq yozishga imkon beradigan sintaksis. Masalan, x += 1 — x = x + 1 ning shakari. Hayotdan o'xshatish: telefondagi "Uyga" yorlig'i. Bosganingizda xarita ochiladi va uy manzili tanlanadi — siz qo'lda qiladigan o'sha ishlar. Yangi imkoniyat yo'q, faqat qulay yo'l.
Bugun class ni "yorliq" sifatida ochamiz: ichida nima bor, va qaysi joylarda u oddiy yorliqdan ko'proq. Bu dars yangi sintaksis o'rgatmaydi — oldingi darslardagi bilimlarni bir-biriga ulaydi. Prototip, new, strict mode va deskriptorlar shu yerda bitta rasmga yig'iladi.
2. Ichkarida — o'sha prototip
2.1 Bitta klass, ikki yozuv
Konstruktor funksiya va new darsida klasslarsiz shunday yozgan edik:
function TaomF(nom, narx) {
this.nom = nom;
this.narx = narx;
}
TaomF.prototype.tavsif = function () {
return `${this.nom}: ${this.narx} so'm`;
};
TaomF.VALYUTA = "so'm";
class Taom {
static VALYUTA = "so'm";
constructor(nom, narx) {
this.nom = nom;
this.narx = narx;
}
tavsif() {
return `${this.nom}: ${this.narx} so'm`;
}
}
const a = new TaomF("Osh", 35000);
const b = new Taom("Osh", 35000);
console.log(typeof TaomF, typeof Taom); // function function
console.log(a.tavsif() === b.tavsif()); // true
console.log(Object.getPrototypeOf(b) === Taom.prototype); // true
console.log(Taom.prototype.constructor === Taom); // trueTo'rt tekshiruv ham bir xil tuzilmani tasdiqladi:
- Klass — funksiya.
class TaomaslidaTaomnomli funksiya yaratadi;constructorichidagi kod — shu funksiyaning tanasi. - Metodlar —
Taom.prototypeda, xuddiTaomF.prototype.tavsif = ...dagidek. - Nusxaning
[[Prototype]]i —Taom.prototype(Prototip zanjiri). prototype.constructor— klassning o'ziga qaytuvchi havola.
static a'zo esa funksiyaning o'z xususiyati: Taom.VALYUTA — TaomF.VALYUTA = ... ning aynan o'zi (Getter/setter va static).
flowchart LR
C["class Taom { ... }"]
F["funksiya Taom<br/>constructor tanasi"]
P["Taom.prototype<br/>tavsif(), get/set"]
S["Taom.VALYUTA<br/>static a'zolar"]
C --> F
C --> P
C --> SDiagrammaga qarang: bitta class e'loni uch joyga taqsimlanadi. Konstruktor kodi — funksiyaga, metodlar va getterlar — prototipga, static lar — funksiyaning o'ziga. Konstruktor funksiya bilan bu uch qismni uchta alohida qatorda qo'lda yozardik.
2.2 Getter ham — eski vosita bilan
Klassdagi get katta() ham yangi mexanizm emas. Xususiyat deskriptorlari darsidagi Object.defineProperty bilan xuddi shunday getter'ni prototipga qo'lda qo'yish mumkin:
function TaomF(nom) {
this.nom = nom;
}
Object.defineProperty(TaomF.prototype, "katta", {
get() {
return this.nom.toUpperCase();
},
});
class Taom {
constructor(nom) {
this.nom = nom;
}
get katta() {
return this.nom.toUpperCase();
}
}
console.log(new TaomF("osh").katta, new Taom("osh").katta); // OSH OSHNatija bir xil, joyi ham bir xil — prototip. Farq faqat yozuvda: klassda getter metod kabi bitta qator bilan e'lon qilinadi, eski yozuvda esa deskriptor obyekti kerak. Klass getter'i ham enumerable: false — defineProperty ning sukut qiymati bilan bir xil.
2.3 extends ham — o'sha zanjir
Meros darsidagi ikki zanjirni eslang. extends siz ularni qo'lda shunday ulardik:
function TaomF(nom) {
this.nom = nom;
}
TaomF.prototype.tavsif = function () {
return this.nom;
};
function IchimlikF(nom, hajm) {
TaomF.call(this, nom); // super(nom) o'rniga
this.hajm = hajm;
}
Object.setPrototypeOf(IchimlikF.prototype, TaomF.prototype);
Object.setPrototypeOf(IchimlikF, TaomF);
IchimlikF.prototype.tavsif = function () {
const ota = TaomF.prototype.tavsif.call(this); // super.tavsif()
return `${ota}, ${this.hajm} l`;
};
const choy = new IchimlikF("Ko'k choy", 0.5);
console.log(choy.tavsif()); // Ko'k choy, 0.5 l
console.log(choy instanceof TaomF); // trueHar qatorning klassdagi juftini toping:
TaomF.call(this, nom)—super(nom).callota funksiyani shuthisbilan chaqirdi.- Birinchi
setPrototypeOf— nusxalar zanjiri, ikkinchisi —staticlar zanjiri.extendsikkalasini bitta so'z bilan qiladi. TaomF.prototype.tavsif.call(this)—super.tavsif(). Ota nomini qo'lda yozish kerak bo'ldi.
Klassdagi yozuv besh-olti qatorni bitta so'zga qisqartiradi. ES2015 dan oldin (2015-yilgacha) hamma JavaScript kodi shunday yozilgan. Eski kutubxonalarda va internetdagi eski javoblarda bu shaklni hali ko'p uchratasiz.
Tekshirib ko'ring: Yuqoridagi kodda ikkinchi
setPrototypeOfqatori o'chirilsa, nima ishlamay qoladi?
Javob
Nusxalar hammasi ishlayveradi: choy.tavsif() va instanceof — birinchi zanjirda. Faqat static meros yo'qoladi: TaomF.dan kabi metod bo'lsa, IchimlikF.dan — undefined. Klasslarda bunday "yarim meros" xatosi bo'lmaydi — extends ikkala zanjirni har doim birga ulaydi.
2.4 DevTools'da ko'ring
Brauzerda ham tuzilma bir xil ko'rinadi. «Bahor» sahifasida DevTools konsolini oching (DevTools bilan debug qilish) va klassni yozing:
class Taom {
#narx = 35000;
constructor(nom) {
this.nom = nom;
}
tavsif() {
return this.nom;
}
}
new Taom("Osh");Natija yonidagi uchburchakni bosib oching. nom: "Osh" va #narx: 35000 — nusxaning o'zida. Pastda [[Prototype]]: Object — uni ham oching: constructor: class Taom va tavsif shu yerda. Yana bir [[Prototype]] — Object.prototype. Bu — «Bitta klass, ikki yozuv» bo'limidagi zanjirning o'zi, brauzer chizgan holda.
Bitta farqqa e'tibor bering: Chrome #narx ni ko'rsatadi, Node esa console.log da ko'rsatmaydi (Taom { nom: 'Osh' }). Bu DevTools'ning debug uchun qulayligi, xolos. Kodingiz baribir #narx ga tashqaridan kira olmaydi.
Amalda shu oynada ko'p savolga javob topasiz: "metod nusxadami yoki prototipdami?", "bu nusxa qaysi klassdan?", "getter qaytargan qiymat nima?" (getter qatoridagi (...) ni bosing — Chrome uni o'shanda chaqiradi).
3. Shakardan ko'proq: besh farq
"Faqat shakar" bo'lganida, ikki yozuv hamma joyda bir xil ishlardi. Ishlamaydi. Quyidagi besh farq — klass eski yozuvga qo'shgan himoyalar.
3.1 new siz chaqirib bo'lmaydi
Konstruktor funksiyani new siz chaqirish — jim xato. Bu blok oddiy skript sifatida (strict'siz) ishga tushadi:
function TaomF(nom) {
this.nom = nom;
}
const osh = TaomF("Osh"); // new unutildi
console.log(osh); // undefined
console.log(globalThis.nom); // Oshnew siz this — global obyekt (Global obyekt va globalThis). Funksiya taomni yaratish o'rniga global nom o'zgaruvchisini yozib yubordi, osh esa undefined. Xato xabari yo'q — xato ancha keyin, boshqa joyda chiqadi.
Klassda esa:
class Taom {
constructor(nom) {
this.nom = nom;
}
}
const osh = Taom("Osh");TypeError: Class constructor Taom cannot be invoked without 'new'Tarjimasi: "Taom klass konstruktorini new siz chaqirib bo'lmaydi". Xato darhol, aynan unutilgan joyda. Klass funksiya bo'lsa ham, uning ichida maxsus belgi bor: "meni faqat new bilan chaqiring".
3.2 Ichi doim strict
Strict mode darsida "use strict" jim xatolarni haqiqiy xatoga aylantirishini ko'rdik. Klass tanasi — "use strict" yozilmagan oddiy skriptda ham — avtomatik strict:
function narxniYoz() {
yangiNarx = 38000; // let unutildi
return "funksiya: jim o'tdi";
}
class Taom {
narxniYoz() {
yangiNarx2 = 38000; // let unutildi
}
}
console.log(narxniYoz());
console.log(typeof yangiNarx); // number
new Taom().narxniYoz();Konsolda:
funksiya: jim o'tdi
numberReferenceError: yangiNarx2 is not definedBu blok ikki narsa chiqardi va keyin xato bilan to'xtadi (saytda "Ishga tushir" bosilsa, xato ham ko'rinadi). Oddiy funksiya let siz o'zgaruvchini jimgina global qilib yaratdi. Klass metodi esa xuddi shu xatoga darhol ReferenceError berdi.
Ikkinchi oqibat — this. Metodni nusxadan ajratib olsangiz, strict'da this — undefined (this yo'qolishi):
class Taom {
constructor(nom) {
this.nom = nom;
}
tavsif() {
return this.nom;
}
}
const tavsif = new Taom("Osh").tavsif;
tavsif();TypeError: Cannot read properties of undefined (reading 'nom')Konstruktor funksiyada (strict'siz) xuddi shu kod this sifatida global obyektni olardi va jimgina undefined qaytarardi. Klass xatoni ko'rinadigan qiladi.
3.3 Metodlar sanalmaydi
function TaomF(nom) {
this.nom = nom;
}
TaomF.prototype.tavsif = function () {};
class Taom {
constructor(nom) {
this.nom = nom;
}
tavsif() {}
}
for (const kalit in new TaomF("Osh")) {
console.log("funksiya:", kalit);
}
for (const kalit in new Taom("Osh")) {
console.log("klass:", kalit);
}Konsolda:
funksiya: nom
funksiya: tavsif
klass: nomfor...in prototip zanjiridagi sanaladigan xususiyatlarni ham aylanadi. TaomF.prototype.tavsif = ... — oddiy tayinlash, u sanaladigan xususiyat yaratadi. Shuning uchun for...in metodni ham "kalit" deb chiqardi. Klass metodlari esa enumerable: false bilan yoziladi (Xususiyat deskriptorlari) — for...in faqat ma'lumotni ko'rsatdi.
OOP tamoyillari darsidagi mixin farqi ham shundan: Object.assign metodlarni oddiy tayinlash bilan qo'shadi — ular sanaladi.
3.4 Hoisting: TDZ
const choy = new Ichimlik();
class Ichimlik {}ReferenceError: Cannot access 'Ichimlik' before initializationfunction e'loni to'liq "ko'tariladi" — uni e'lon qatoridan oldin ham chaqirish mumkin. Klass esa let va const kabi TDZ da turadi (Temporal Dead Zone): nomi ma'lum, lekin e'lon qatorigacha unga tegib bo'lmaydi. Tuzatish: klassni ishlatishdan oldin e'lon qiling — odatda faylning boshida.
3.5 super — [[HomeObject]] ga bog'langan
Bu farq eng nozigi. Eski yozuvda ota metodini nomi bilan chaqirardik: TaomF.prototype.tavsif.call(this). Klassda esa super.tavsif() — ota nomi yo'q. super otani qayerdan biladi?
Javob: klass ichida yozilgan har metod yashirin [[HomeObject]] ("uy obyekti") havolasini oladi — u qaysi prototipda yozilgani. super.tavsif() ning qidiruvi [[HomeObject]] ning [[Prototype]] idan boshlanadi. this dan emas!
Buni metodni boshqa klassga "qarzga" berib ko'rish mumkin:
class Taom {
salom() {
return "taom";
}
}
class Ichimlik extends Taom {
salom() {
return `ichimlik → ${super.salom()}`;
}
}
class Xodim {
salom() {
return "xodim";
}
}
class Ofitsiant extends Xodim {}
Ofitsiant.prototype.salom = Ichimlik.prototype.salom;
console.log(new Ofitsiant().salom()); // ichimlik → taomKutilgan javob "ichimlik → xodim" edi: metod endi Ofitsiant da, uning otasi — Xodim. Lekin chiqdi "taom". Metodning [[HomeObject]] i — Ichimlik.prototype, u yerdan bir qavat yuqorida esa Taom.prototype. super metod qayerga ko'chirilganiga qaramaydi — u tug'ilgan uyini eslaydi.
Qadamma-qadam ko'ring — Keyingi tugmasini bosib, qidiruv qayerdan boshlanishiga qarang:
Amalda bu kamdan-kam muammo bo'ladi — metodlarni klasslar orasida ko'chirish odatiy ish emas. Lekin mixinlarda super ishlatsangiz yoki eski kodni klassga o'tkazsangiz, aynan shu sababli "noto'g'ri ota" chaqirilishi mumkin. Qoida: super — yozilgan joyning otasi.
3.6 Yana ikki kichik himoya
Klass metodi — konstruktor emas. Eski yozuvda TaomF.prototype.tavsif oddiy function edi, uni new bilan ham chaqirsa bo'lardi — ma'nosiz bo'lsa ham. Klass metodida esa:
class Taom {
tavsif() {
return "osh";
}
}
const osh = new Taom();
new osh.tavsif();TypeError: osh.tavsif is not a constructorTarjimasi: "osh.tavsif — konstruktor emas". Metod faqat chaqirish uchun, yangi obyekt yaratish uchun emas. Arrow funksiyalar ham xuddi shunday — ularda ham prototype yo'q.
Ikkinchisi: klass ichida klassning o'z nomi o'zgarmas — xuddi const kabi:
class Taom {
buz() {
Taom = null;
}
}
new Taom().buz();TypeError: Assignment to constant variable.Tarjimasi: "o'zgarmas o'zgaruvchiga qiymat berib bo'lmaydi". Bu himoya static dan kabi metodlar uchun: ular ichidagi new Taom(...) doim aynan shu klassni ko'rsatadi. Klass tashqarisida esa Taom nomi let kabi — uni boshqa qiymatga almashtirish mumkin, lekin klass ichidagi kod buni sezmaydi.
4. Maydonlar va #private: haqiqiy yangilik
4.1 Maydon setter'ni chaqirmaydi
Class maydonlari — ES2022 qo'shimchasi. Ular konstruktordagi this.x = ... ga o'xshaydi, lekin bitta farq bilan:
class Taom {
set narx(qiymat) {
console.log(`setter: ${qiymat}`);
}
}
class Ichimlik extends Taom {
narx = 5000; // maydon
}
class Kompot extends Taom {
constructor() {
super();
this.narx = 8000; // tayinlash
}
}
const choy = new Ichimlik();
new Kompot();
console.log(Object.keys(choy)); // [ 'narx' ]Konsolda:
setter: 8000
[ 'narx' ]Konsolda setter: 5000 yo'q — Ichimlik yaratilganda setter umuman chaqirilmadi.
Kompot dagi this.narx = 8000 — oddiy tayinlash. U zanjir bo'ylab setter'ni topdi va chaqirdi. Ichimlik dagi narx = 5000 maydoni esa setter'ni chetlab o'tdi: u nusxaning o'ziga yangi xususiyat yaratdi. Maydon "yoz" emas, "e'lon qil" (Object.defineProperty kabi) ma'nosida ishlaydi.
Amaliy xulosa: ota klassda setter bo'lsa, bola klassda xuddi shu nomli maydon e'lon qilmang — tekshiruv ishlamay qoladi. Kerak bo'lsa, konstruktorda this.narx = ... yozing.
Tekshirib ko'ring: Getter/setter va
staticdarsidagiTaomdaset narxmanfiy narxni rad etardi.class Arzon extends Taom { narx = -5; }yozilsa,new Arzon("Somsa").narxnima bo'ladi?
Javob
-5. Maydon setter'ni chaqirmay, nusxaning o'ziga narx xususiyatini yaratdi. Endi bu nusxada otaning getter va setter'i ham ishlamaydi — o'z narx i ularni soyalab qo'ydi. Tekshiruv jimgina chetlab o'tildi. Ota konstruktori this.narx = narx qilgan bo'lsa ham, bola maydoni undan keyin yoziladi va qiymatni almashtiradi.
4.2 #private — shakar emas
Klassdagi ko'p narsani eski sintaksisda yozsa bo'ladi. #narx ni esa — yo'q. _narx konventsiyasi faqat "iltimos, tegmang" degan kelishuv, closure esa har nusxa uchun alohida funksiyalar yaratadi (Private # maydon). #maydon esa:
- oddiy xususiyat emas —
Object.keys,for...in,JSON.stringifyuni umuman ko'rmaydi; - klass tashqarisidan unga murojaat — sintaksis xatosi, kod hatto ishga tushmaydi;
- prototip zanjirida meros olinmaydi — bola klass otaning
#narxiga to'g'ridan-to'g'ri kira olmaydi, faqat ota metodlari orqali.
Shuning uchun aniqroq gap: "class — prototip ustidagi shakar, ustiga bir necha himoya va bitta yangi imkoniyat (#)".
4.3 Farqlar bitta jadvalda
| Konstruktor funksiya | class |
|
|---|---|---|
new siz chaqiruv |
jim ishlaydi, global'ni buzadi | TypeError |
| Ichki kod | strict faqat "use strict" bilan |
doim strict |
| Prototip metodlari | for...in da ko'rinadi |
ko'rinmaydi |
| E'londan oldin ishlatish | mumkin (hoisting) | TDZ — ReferenceError |
| Ota metodi | Ota.prototype.m.call(this) |
super.m() ([[HomeObject]]) |
| Private holat | _ kelishuvi yoki closure |
#maydon |
Tekshirib ko'ring: Hamkasbingiz "klass — faqat shakar, demak eski konstruktor funksiyani klassga ko'chirish xavfsiz" deydi. Qaysi uch holatda ko'chirilgan kod endi xato beradi?
Javob
- Kodning biror joyida
newsiz chaqiruv qolgan bo'lsa —TypeError. 2) Metodlar ichidaletsiz o'zgaruvchi yokithisyo'qolishi bo'lsa — strict tufayliReferenceError/TypeError. 3) Klass e'lonidan oldin ishlatilsa — TDZ. Bonus: prototip metodlarinifor...inbilan aylanadigan kod endi ularni ko'rmaydi. Bu xatolarning hammasi aslida eski kodda yashirin turgan xatolar — klass ularni ko'rinadigan qildi.
5. Ko'p uchraydigan xatolar
5.1 new ni unutish
TypeError: Class constructor Taom cannot be invoked without 'new' — «new siz chaqirib bo'lmaydi» bo'limida. Xabarda "Class constructor" bo'lsa — chaqiruv joyiga new qo'shing. Factory metod (Taom.dan(...)) ishlatayotgan bo'lsangiz, new uning ichida.
5.2 Klass nusxasini nusxalash yoki JSON'ga aylantirish
class Taom {
#narx = 35000;
constructor(nom) {
this.nom = nom;
}
tavsif() {
return this.nom;
}
}
const osh = new Taom("Osh");
console.log(JSON.stringify(osh)); // {"nom":"Osh"}
const nusxa = structuredClone(osh);
console.log(nusxa.tavsif); // undefinedJSON.stringify faqat nusxaning o'z sanaladigan xususiyatlarini oldi: #narx va metodlar yo'qoldi. structuredClone ham shunday: nusxa — oddiy obyekt, klassdan ajralgan, metodlarsiz. Tuzatish — toJSON va factory metod; buni «Vazifalar qadami» da vazifalar ga qo'shamiz.
5.3 Klass ichidagi metodni callback qilib berish
«Ichi doim strict» bo'limidagi Cannot read properties of undefined (reading 'nom'). Klass metodlarini addEventListener yoki map ga berishda arrow bilan o'rang yoki bind qiling (this yo'qolishi).
5.4 Ota setter'i bilan bir xil nomli maydon
«Maydon setter'ni chaqirmaydi» bo'limidagi holat: xato xabari yo'q, tekshiruv esa ishlamaydi. Bunday xato odatda kech topiladi — noto'g'ri qiymat bazaga yoki ekranga chiqqanda. Tuzatish: bola klassda otaning accessor xususiyati nomi bilan maydon e'lon qilmang; boshlang'ich qiymatni super(...) ga argument qilib bering yoki konstruktorda tayinlang.
5.5 "Klass — boshqa tillardagidek" deb o'ylash
Java yoki C# dan kelgan dasturchilar ko'pincha metod nusxada saqlanadi, this doim nusxa, klass e'londan oldin ham bor deb o'ylaydi. JavaScript'da uchalasi ham boshqacha: metod — prototipda, this — chaqiruvga bog'liq, klass — TDZ'da. Kutilmagan xulq ko'rsangiz, avval "bu ichkarida qanday prototip kodi?" deb so'rang — javob odatda shu darsdagi jadvalda.
6. Mashqlar
1-mashq (oson): Klassni "ochib" yozing
Quyidagi klassni konstruktor funksiya va prototype bilan qayta yozing. static ni ham unutmang. So'ng new siz chaqiruv natijasini ikkalasida solishtiring (faqat fikrda — kodda new bilan qoldiring).
class Bron {
static MAKS_ODAM = 20;
constructor(ism, odam) {
this.ism = ism;
this.odam = odam;
}
matn() {
return `${this.ism}, ${this.odam} kishi`;
}
}Yechim
function Bron(ism, odam) {
this.ism = ism;
this.odam = odam;
}
Bron.MAKS_ODAM = 20;
Bron.prototype.matn = function () {
return `${this.ism}, ${this.odam} kishi`;
};
const bron = new Bron("Malika", 4);
console.log(bron.matn(), Bron.MAKS_ODAM); // Malika, 4 kishi 20new siz: klass — TypeError; funksiya — strict'siz skriptda global ism va odam ni yozib, undefined qaytaradi. Modul faylda (.mjs) esa this — undefined va Cannot set properties of undefined xatosi chiqadi.
2-mashq (o'rta): Nima chiqadi?
Kodni ishga tushirmasdan oldin har qatorning natijasini yozing, keyin tekshiring.
class Taom {
turi = "taom";
constructor(nom) {
this.nom = nom;
}
tavsif() {
return this.nom;
}
get katta() {
return this.nom.toUpperCase();
}
}
const osh = new Taom("Osh");
console.log(Object.keys(osh)); // [ 'turi', 'nom' ]
console.log(Object.getOwnPropertyNames(Taom.prototype));
// [ 'constructor', 'tavsif', 'katta' ]
console.log(JSON.stringify(osh)); // {"turi":"taom","nom":"Osh"}
console.log(osh.hasOwnProperty("tavsif")); // falseYechim
Object.keys(osh)—[ 'turi', 'nom' ]: maydon (turi) konstruktor tanasidan oldin yoziladi, shuning uchun birinchi.- Prototipda —
constructor,tavsifva getterkatta. Ular sanalmaydi, lekingetOwnPropertyNamessanalmaydiganlarni ham ko'rsatadi. - JSON'da faqat o'z ma'lumot xususiyatlari:
kattagetteri prototipda — kirmadi. tavsif— nusxaning o'zida emas, prototipda:false.
3-mashq (qiyin): super ni qo'lda
Ichimlik klassini klass sintaksisisiz yozing: TaomF dan meros, tavsif() ota natijasiga " (ichimlik)" qo'shsin, static dan(m) ham meros bo'lsin va chaqirgan klass nusxasini yaratsin. Ishora: static da new this(...); ota metodi — call bilan.
Yechim
function TaomF(nom) {
this.nom = nom;
}
TaomF.prototype.tavsif = function () {
return this.nom;
};
TaomF.dan = function (m) {
return new this(m.nom);
};
function IchimlikF(nom) {
TaomF.call(this, nom);
}
Object.setPrototypeOf(IchimlikF.prototype, TaomF.prototype);
Object.setPrototypeOf(IchimlikF, TaomF);
IchimlikF.prototype.tavsif = function () {
return `${TaomF.prototype.tavsif.call(this)} (ichimlik)`;
};
const kompot = IchimlikF.dan({ nom: "Kompot" });
console.log(kompot.tavsif());
console.log(kompot instanceof IchimlikF);Konsolda:
Kompot (ichimlik)
trueIchimlikF.dan ikkinchi setPrototypeOf tufayli topildi; ichida this — IchimlikF, shuning uchun nusxa ichimlik. TaomF.prototype.tavsif.call(this) — super.tavsif() ning qo'lda yozilgan shakli. Bu kod ishlaydi, lekin klassdagidan ikki baravar uzun va har ota nomi qo'lda — extends va super nega qo'shilgani shu yerda ko'rinadi.
4-mashq: Vazifalar qadami — royxatni JSON'ga qaytarish
refactor/vazifalar-royxati branch'ida davom etamiz. O'tgan darsdagi sinov ro'yxatiga qayta qarang: unda bitta tugma yo'q edi — eksport (JSON asoslari). U holatni JSON.stringify bilan matnga aylantiradi. Private # maydon darsida Vazifa ga aynan shu uchun toJSON qo'shgan edik. 11-qismda saqlash ham shu yo'l bilan ishlaydi.
Hozir tekshirib ko'ring. «JSON qilib chiqarish» tugmasini bosing, keyin DevTools konsolida yozing:
JSON.stringify(holat.vazifalar.top(1));
// '{"id":1,"matn":"Non olish","bajarildi":false}'
JSON.stringify(holat.vazifalar);
// '{}'Bitta vazifa to'g'ri — uning toJSON i ishlayapti. Butun royxat esa bo'sh obyekt, eksport maydonida ham "vazifalar": {}! O'tgan darsgacha holat.vazifalar massiv edi va JSON.stringify har elementning toJSON ini chaqirardi. Endi u VazifalarRoyxati nusxasi. Unda faqat #vazifalar va #yangiId bor — ikkalasi private, JSON.stringify ularni ko'rmaydi.
Bu — «Klass nusxasini JSON'ga aylantirish» xatosi. Refaktoring ro'yxatdagi xulqni o'zgartirmadi, lekin eksportni jimgina buzdi: konsolda xato yo'q, ro'yxat to'g'ri chiziladi. Sinov ro'yxatida bo'lmagan imkoniyat — sinalmagan imkoniyat.
Vazifa: VazifalarRoyxati ga toJSON() metodini qo'shing (JSON chuqur darsidagi toJSON) — u vazifalarning oddiy obyektlar massivini qaytarsin. So'ng VazifalarRoyxati.dan(JSON.parse(matn)) royxatni to'liq tiklashini tekshiring. Ilova xulqi o'zgarmasin.
Yechim
toJSON() {
return this.#vazifalar.map((v) => v.toJSON());
}Royxat ichki massivni emas, yangi massivni qaytaradi — OOP tamoyillari darsidagi "Ichki massivni tashqariga berib yuborish" xatosidan himoya. toJSON ni har kim chaqira oladi, JSON.stringify esa faqat birinchisi.
Node'da to'liq aylanishni tekshiring — matnga va qaytib. Klasslar qisqartirilgan (faqat aylanishga kerakli qismi):
class Vazifa {
id;
bajarildi;
#matn;
constructor(id, matn, bajarildi = false) {
this.id = id;
this.#matn = matn;
this.bajarildi = bajarildi;
}
static dan(m) {
return new Vazifa(m.id, m.matn, m.bajarildi);
}
toJSON() {
const { id, bajarildi } = this;
return { id, matn: this.#matn, bajarildi };
}
}
class VazifalarRoyxati {
#vazifalar = [];
static dan(malumotlar) {
const yangi = new VazifalarRoyxati();
yangi.#vazifalar = malumotlar.map((m) => Vazifa.dan(m));
return yangi;
}
toJSON() {
return this.#vazifalar.map((v) => v.toJSON());
}
}
const royxat = VazifalarRoyxati.dan([
{ id: 1, matn: "Non olish", bajarildi: false },
{ id: 2, matn: "DOM darsini takrorlash", bajarildi: true },
]);
const matn = JSON.stringify(royxat);
console.log(matn);
const tiklangan = VazifalarRoyxati.dan(JSON.parse(matn));
console.log(JSON.stringify(tiklangan) === matn); // trueKonsolda:
[{"id":1,"matn":"Non olish","bajarildi":false},{"id":2,"matn":"DOM darsini takrorlash","bajarildi":true}]
trueIkki tomonlama yo'l tayyor: toJSON — klassdan matnga, dan — matndan klassga. Sizning faylingizda #yangiId ham bor — u saqlanmaydi, dan generatorni eng katta id dan qayta yasaydi.
Brauzerni yangilab, qayta tekshiring. Konsolda JSON.stringify(holat.vazifalar) uch vazifani to'liq ko'rsatadi, eksport maydonida esa yana uch vazifa — refaktoringdan oldingi natija bilan harfma-harf bir xil. JSON.stringify royxatning toJSON ini o'zi chaqirdi, eksport kodiga tegmadik. Qolgan hamma narsa (qo'shish, belgilash, tahrir, filtr, fokus) — avvalgidek.
Commit: Ro'yxat: VazifalarRoyxati ga toJSON qo'sh.
7. Real ishda
- Eski kod. 2015-yilgacha yozilgan kutubxonalar va ko'p brauzer polyfill'lari — konstruktor funksiya va
prototypeda. Ularni o'qish va klassga ko'chirish — real loyihalardagi tez-tez uchraydigan vazifa. Bu darsdagi farqlar ro'yxati — ko'chirish uchun nazorat varag'i. - Transpilyatorlar. Transpilyator — yangi JavaScript kodini eski brauzer tushunadigan kodga o'giradigan dastur. Babel va TypeScript (15–16-qismlarda o'rganamiz, hozir bilish shart emas) yangi kodni eski brauzerlar uchun qayta yozganda
classni aynan shu darsdagidek funksiya vaprototypega aylantiradi —newtekshiruvi uchun maxsus yordamchi bilan. - Tezlik. JavaScript dvigatellari (Chrome'dagi V8) bir xil tuzilmadagi obyektlarni tezroq ishlaydi. Klass nusxalari doim bir xil maydonlar bilan, bir xil tartibda yaratiladi — shuning uchun ular dvigatel uchun qulay. Bu mavzuni JS dvigateli ichida darsida chuqurroq ko'ramiz, hozir bilish shart emas.
- Saqlash va tarmoq. Klass nusxasi JSON'ga o'z-o'zidan to'g'ri aylanmaydi. Ma'lumotni serverga yuborish yoki saqlash (11-qism) —
toJSON+ factory metod juftligi bilan. - Intervyu. "
classfaqat sintaktik shakarmi?" — o'rta darajadagi sevimli savol. Yaxshi javob: "Asosan ha — o'sha prototip; lekinnewmajburiy, ichi strict, metodlar sanalmaydi, TDZ,super[[HomeObject]]orqali,#privateesa umuman yangi."
Xulosa
class— funksiya +prototype+staticxususiyatlar; nusxa zanjiri konstruktor funksiyadagidek.extends— ikkisetPrototypeOf,super(...)—Ota.call(this, ...),super.m()—Ota.prototype.m.call(this).- Klass
newsiz chaqirilmaydi, ichi doim strict, metodlari sanalmaydi, e'londan oldin TDZ'da. supermetod yozilgan joyning otasini qidiradi ([[HomeObject]]),thisning emas.- Maydon e'lon qiladi (setter'ni chetlab o'tadi), konstruktordagi tayinlash esa setter'ni chaqiradi.
#private— shakar emas, yangi imkoniyat; JSON uni ko'rmaydi — kerak bo'lsatoJSONyozing.
Keyingi dars: JS'da dizayn naqshlari — kuzatuvchi, modul, factory, singleton va boshqa tayyor yechim qoliplarini «Bahor» misollarida ko'ramiz va vazifalar da render'ni kuzatuvchi orqali chaqiramiz.
Manbalar
- MDN: "Classes", "Public class fields", "super", "Strict mode" — developer.mozilla.org
- ECMAScript 2025 Language Specification: "ClassDefinitionEvaluation", "MakeMethod" (
[[HomeObject]]), "DefineField" — tc39.es/ecma262
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!