IlmHamroh
JavaScript Full-stack/8-qism. JavaScript asoslari62/62-dars30 daqiqa
Mundarija (35)

JavaScript yakuniy loyiha: konsolda oylik byudjet dasturi

Qisqacha: Bu darsda oylik daromad va xarajatlarni hisoblaydigan Node.js dasturini noldan yozasiz. Dastur noto'g'ri yozuvlarni ajratadi, har toifaga ketgan pulni va uning ulushini hisoblaydi, limitdan oshganini ogohlantiradi va hisobot chiqaradi. Hammasi 08-qism bilimlari bilan: massiv, obyekt, toza funksiya, spread, padStart va console.table.

Bu darsda

  • Kichik funksiyalarga bo'lingan to'liq dasturni bosqichma-bosqich yozasiz.
  • Noto'g'ri ma'lumotni (manfiy summa, matn, kasr) topib, o'tkazib yuborasiz.
  • Hisob-kitobni toza funksiyalarga, ekranga chiqarishni esa bitta joyga yig'asiz.
  • Dasturdagi NaN xatosini debug bilan topasiz.
  • Dasturni mashqlar reposiga commit qilib, GitHub'ga yuborasiz va 100 ballik ro'yxat bo'yicha baholaysiz.

Oldin bilishingiz kerak: Pul va aniq hisob-kitob, Pure funksiya va side effect, Obyekt bo'ylab yurish, Spread va rest operatori.

1. Nega bu kerak?

Oy boshida Azizning kartasiga maosh tushdi. Oy oxirida u hayron: "Pul qayerga ketdi?" Xarajatlar telefondagi eslatmada yozilgan: "Bozor — 1 200 000", "Taksi — 95 000". Ro'yxat bor, lekin javob yo'q. Qaysi toifaga eng ko'p ketdi? Kafe uchun belgilagan chegaradan oshdimi? Oy oxirida qancha qoladi?

Har oy kalkulyator bilan hisoblash uzoq va zerikarli. Dasturchi bunday ishni bir marta dasturga aylantiradi, keyin faqat ro'yxatni yangilaydi. Bugun shu dasturni yozamiz. Yangi mavzu yo'q — 61 darsda o'rganganlaringiz bitta foydali dasturga yig'iladi. G'isht va sement alohida — shunchaki material. Ulardan uy qurilsa — boshqa narsa.

2. Loyiha talablari

2.1 Dastur nima qiladi

node byudjet.js buyrug'idan keyin dastur:

  1. Xarajatlarni tekshiradi: summa son emas, butun emas yoki manfiy bo'lsa — ogohlantirib, o'tkazib yuboradi. Toifasiz yozuv — "boshqa" toifasiga.
  2. Daromad, xarajat va qolgan pulni hisoblaydi.
  3. Har toifa yig'indisini va ulushini (foizini) hisoblaydi, limitdan oshganini ogohlantiradi.
  4. Ustunlarga tekislangan hisobot va console.table jadvalini chiqaradi.

2.2 Qoidalar

  • Pul — butun so'mda. Kasr son pulda xato beradi (Pul va aniq hisob-kitob). U darsda pulni tiyinda saqlagan edik. Kundalik xarajatda tiyin yo'q, shuning uchun bu loyihada birlik — so'm, kasr summa esa yozishdagi xato. Foiz — butun son, bo'lish eng oxirida.
  • Bitta funksiya — bitta ish. Har bosqich alohida funksiya.
  • Hisob-kitob funksiyalari toza (pure). Ular faqat qiymat qaytaradi, ekranga hech narsa yozmaydi (Pure funksiya va side effect).
  • Asl ma'lumot o'zgarmaydi. Yangi obyekt kerak bo'lsa, spread bilan nusxa olinadi.
  • Yon ta'sir — faqat bitta funksiyada. console.log, console.warn va console.table faqat dasturniIshgaTushir ichida.
  • Faqat 08-qism bilimlari. Ma'lumot kod ichida yoziladi. Fayldan o'qishni keyingi qismlarda o'rganamiz.

Tekshirib ko'ring: Nega hisob-kitob funksiyalari ekranga o'zi yozmasligi kerak? Axir console.log bilan darrov ko'rinardi-ku.

Javob

Ekranga yozadigan funksiyani boshqa joyda ishlatib bo'lmaydi. Toza funksiya natijani qaytaradi — uni ekranga ham, ertaga Telegram'ga ham berish mumkin. Tekshirish ham qulay: bir xil kirish — bir xil natija. Bu Pure funksiya darsidagi "oshpaz va ofitsiant" qoidasi.

3. Reja: ma'lumot qayerdan qayerga oqadi

Kod yozishdan oldin — reja. Diagrammada har to'rtburchak — bitta funksiya. Yashillari toza (faqat hisoblaydi), to'q sariqlari ekranga chiqaradi. Ma'lumot strelkalar bo'ylab yuqoridan pastga oqadi. Kichik yordamchilar (foizHisobla, somYoz) diagrammada yo'q — ular pastdagi jadvalda.

flowchart TD
  M["Ma'lumot: xarajatlar"] --> T["xarajatlarniTekshir"]
  T -->|xatolar| W["console.warn"]
  T -->|togrilar| J["jamiHisobla"]
  T -->|togrilar| Y["toifalarBoyicha"]
  J --> H["hisobotTuz"]
  Y --> H
  Y --> O["oshganToifalar"]
  Y --> D["jadvalTuz"]
  H --> L["console.log"]
  O --> L
  D --> C["console.table"]
  classDef toza fill:#e8f1ec,stroke:#1f6f4a
  classDef yon fill:#fdeee6,stroke:#b4461a
  class T,J,Y,H,O,D toza
  class W,L,C yon

Ish olti bosqichda boradi:

# Bosqich Funksiyalar Tayanch dars
1 Ma'lumot modeli — Obyekt
2 Tekshirish xatoSababi, xarajatlarniTekshir Matn ↔ son
3 Yig'indilar jamiHisobla, toifalarBoyicha for...of
4 Foiz va limit foizHisobla, oshganToifalar Math
5 Hisobot somYoz, hisobQatori, hisobotTuz, jadvalTuz padStart
6 Yig'ish dasturniIshgaTushir Pure funksiya

Har bosqichda avval o'zingiz yozib ko'ring, keyin yechim bilan solishtiring. Qiynalsangiz — yechimni o'qing va uni ko'rmasdan qayta yozing.

Kod bloklari bir-birining davomi: oldingi bloklardagi o'zgaruvchi va funksiyalarni ishlatadi. Saytdagi "Ishga tushir" tugmasi oldingi bloklarni jim bajaradi va faqat shu blok natijasini ko'rsatadi.

4. 1-bosqich: ma'lumot modeli

Har xarajat — bitta obyekt ("kartochka"): toifa, summa va izoh. Hamma kartochkalar — bitta massivda. Bu obyektlar massivi — real dasturlarda ma'lumot deyarli doim shunday keladi. byudjet.js faylini yarating va boshiga ma'lumotni yozing:

js
const oy = "2026-yil oktabr";

const daromadlar = [
  { manba: "Oylik maosh", summa: 6_000_000 },
  { manba: "Frilans: sayt", summa: 1_500_000 },
];

const xarajatlar = [
  { toifa: "ijara", summa: 2_500_000, izoh: "Kvartira" },
  { toifa: "ovqat", summa: 1_200_000, izoh: "Bozor" },
  { toifa: "transport", summa: 180_000, izoh: "Metro, avtobus" },
  { toifa: "ovqat", summa: 420_000, izoh: "Supermarket" },
  { toifa: "aloqa", summa: 150_000, izoh: "Internet, telefon" },
  { toifa: "kafe", summa: 260_000, izoh: "Do'stlar bilan" },
  { toifa: "transport", summa: 95_000, izoh: "Taksi" },
  { toifa: "kafe", summa: 140_000, izoh: "Tug'ilgan kun" },
  { toifa: "kiyim", summa: -350_000, izoh: "Krossovka" },
  { toifa: "kitob", summa: "85000", izoh: "JS kitobi" },
  { summa: 40_000, izoh: "Sartarosh" },
  { toifa: "ovqat", summa: 37_500.5, izoh: "Non" },
];

const limitlar = {
  ijara: 2_500_000,
  ovqat: 1_500_000,
  transport: 300_000,
  aloqa: 150_000,
  kafe: 300_000,
};

console.log(xarajatlar.length); // 12
  • 2_500_000 — pastki chiziq faqat ko'z uchun, JavaScript uni 2500000 deb o'qiydi (Son yozish shakllari).
  • Oxirgi to'rtta yozuv ataylab buzuq. Hayotda ma'lumot doim toza kelmaydi: minus tasodifan bosiladi, summa qo'shtirnoqda yoziladi. Dastur bunda buzilmasligi kerak.
  • Sartaroshda toifa yo'q — yozuv.toifa undefined bo'ladi. Uni "boshqa" toifasiga qo'yamiz.
  • limitlar — obyekt, massiv emas. Limit toifa nomi bilan darrov topiladi: limitlar[toifa] (Obyekt).

Maslahat: Summalarni o'z xarajatlaringizga almashtirsangiz, dastur siz uchun ishlaydi. Lekin GitHub'dagi repo ochiq bo'lishi mumkin — shaxsiy summalarni emas, namunani commit qiling.

5. 2-bosqich: noto'g'ri yozuvlarni ajratish

5.1 Vazifa

Ikki funksiya yozing:

  1. xatoSababi(summa) — summa to'g'ri bo'lsa null, aks holda sababini matn bilan qaytaradi. Tekshiruvlar: son emasmi (typeof), butunmi (Number.isInteger), musbatmi.
  2. xarajatlarniTekshir(royxat) — yozuvlarni ikki yangi massivga (togrilar, xatolar) ajratib, bitta obyektda qaytaradi. Toifasiz yozuvga "boshqa" qo'yadi.

Ishora: ?? — chap tomoni undefined bo'lsa o'ng tomonni oladi (?? va ?.). Sikldagi navbatdagi yozuvni tashlab ketish — continue (break va continue).

Yechim
js
function xatoSababi(summa) {
  if (typeof summa !== "number") {
    return "summa son emas";
  }
  if (!Number.isInteger(summa)) {
    return "summa butun son emas";
  }
  if (summa <= 0) {
    return "summa musbat emas";
  }
  return null;
}

function xarajatlarniTekshir(royxat) {
  const togrilar = [];
  const xatolar = [];
  for (const yozuv of royxat) {
    const sabab = xatoSababi(yozuv.summa);
    if (sabab !== null) {
      xatolar.push({ ...yozuv, sabab });
      continue;
    }
    togrilar.push({ ...yozuv, toifa: yozuv.toifa ?? "boshqa" });
  }
  return { togrilar, xatolar };
}

console.log(xatoSababi(150_000)); // null
console.log(xatoSababi("85000")); // summa son emas
console.log(xatoSababi(37_500.5)); // summa butun son emas
console.log(xatoSababi(-350_000)); // summa musbat emas

Qatorma-qator:

  • typeof summa !== "number" birinchi turadi. "85000" — satr. Uni songa aylantirmaymiz: ma'lumot noto'g'ri yozilgan, egasi tuzatsin (typeof).
  • Number.isInteger kasrni ushlaydi. NaN va Infinity uchun ham false beradi (Matn ↔ son).
  • summa <= 0 — manfiy va nol summa.
  • Har tekshiruv — erta qaytish: sabab topilsa, qolganini tekshirish shart emas (return va erta qaytish).
  • { ...yozuv, sabab } — yozuv nusxasi va yangi sabab xususiyati (sabab: sabab ning qisqasi — Shorthand).
  • { ...yozuv, toifa: yozuv.toifa ?? "boshqa" } — nusxa, toifa ustidan yoziladi: spread'dan keyingi kalit ustun (Spread va rest).

Funksiya natija obyekt qaytaradi. Undan ikki massivni nuqta bilan olamiz:

js
const tekshiruv = xarajatlarniTekshir(xarajatlar);

console.log(tekshiruv.togrilar.length); // 9
console.log(tekshiruv.xatolar.length); // 3
console.log(tekshiruv.togrilar[8].toifa); // boshqa
console.log(xarajatlar[10].toifa); // undefined

12 ta yozuvdan 9 tasi to'g'ri, 3 tasi buzuq. Oxirgi qatorga qarang: asl massivdagi sartarosh yozuvida toifa hali ham undefined. Biz aslini emas, nusxasini o'zgartirdik.

5.2 Nega nusxa muhim?

Spread'siz ham bo'lardi: yozuv.toifa = "boshqa" va togrilar.push(yozuv). Lekin yozuv — asl obyektga havola, nusxa emas (Havola semantikasi). Bunday funksiya asl ma'lumotni jimgina o'zgartiradi — bu yon ta'sir. { ...yozuv } — bunga qarshi eng arzon himoya. ko'rinishini «Ko'p uchraydigan xatolar» bo'limida ishga tushirasiz.

Diqqat: Spread — sayoz nusxa, faqat birinchi qavat (Sayoz va chuqur nusxalash). Yozuvlarimizda ichma-ich obyekt yo'q, shuning uchun spread yetarli. Ichida obyekt bo'lsa, structuredClone kerak bo'lardi.

Tekshirib ko'ring: xatoSababi(NaN) nima qaytaradi va nega?

Javob

"summa butun son emas". typeof NaN — "number" (Number turi), shuning uchun birinchi tekshiruvdan o'tadi. Number.isInteger(NaN) esa false — ikkinchi tekshiruv ushlaydi.

6. 3-bosqich: jami va toifa bo'yicha yig'indi

6.1 Vazifa

  1. jamiHisobla(royxat) — summa li yozuvlar massivining yig'indisi. Daromadlar uchun ham, xarajatlar uchun ham ishlasin.
  2. toifalarBoyicha(royxat) — obyekt qaytaradi: kalit — toifa, qiymat — shu toifa yig'indisi.

Ishora: bu yerda obyekt hisoblagich bo'ladi. Toifa birinchi uchraganda u obyektda hali yo'q.

Yechim
js
function jamiHisobla(royxat) {
  let jami = 0;
  for (const { summa } of royxat) {
    jami += summa;
  }
  return jami;
}

function toifalarBoyicha(royxat) {
  const yigindilar = {};
  for (const { toifa, summa } of royxat) {
    yigindilar[toifa] = (yigindilar[toifa] ?? 0) + summa;
  }
  return yigindilar;
}

console.log(jamiHisobla(daromadlar)); // 7500000
console.log(jamiHisobla(tekshiruv.togrilar)); // 4985000
  • for (const { summa } of royxat) — sikl o'zgaruvchisida destructuring: har yozuvdan faqat summa olinadi (Parametrda destructuring). Shuning uchun funksiya daromad uchun ham ishlaydi.
  • yigindilar[toifa] — kvadrat qavs, chunki kalit nomi o'zgaruvchida (Obyekt). yigindilar.toifa esa "toifa" degan kalit yaratardi.
  • (yigindilar[toifa] ?? 0) + summa — eng muhim qator. Toifa birinchi uchraganda u yerda undefined, ?? 0 uni 0 ga almashtiradi.

Hisoblagich qanday to'lishini kichik ro'yxatda ko'ring. Keyingi tugmasini bosib, yigindilar ga qarang: "ovqat" ikkinchi marta kelganda yangi kalit ochilmaydi, eski qiymatga qo'shiladi.

Endi haqiqiy ma'lumot bilan:

js
const yigindilar = toifalarBoyicha(tekshiruv.togrilar);

console.log(Object.keys(yigindilar).length); // 6
console.log(yigindilar.ovqat); // 1620000
console.log(yigindilar.boshqa); // 40000

Olti toifa. "Ovqat" — ikkita to'g'ri yozuvdan: 1 200 000 + 420 000. Kasrli non tekshiruvdan o'tmagan edi.

Bo'sh joyni to'ldiring. undefined ?? 0 ning natijasi — [:0]. Agar ?? 0 ni olib tashlasak, undefined + 260000 natijasi — [:NaN].

7. 4-bosqich: foiz va limit

7.1 Vazifa

  1. foizHisobla(qism, jami) — qism jami ning necha foizi — butun son. jami 0 bo'lsa — 0.
  2. oshganToifalar(yigindilar, limitlar) — limitdan oshgan toifalar: { toifa, sarf, limit, ortiqcha } massivi. Limitsiz toifa tekshirilmaydi.

Ishora: obyektni kalit-qiymat juftliklari bilan aylanish — Object.entries (Obyekt bo'ylab yurish).

Yechim
js
function foizHisobla(qism, jami) {
  if (jami === 0) {
    return 0;
  }
  return Math.round((qism * 100) / jami);
}

function oshganToifalar(yigindilar, limitlar) {
  const oshganlar = [];
  for (const [toifa, sarf] of Object.entries(yigindilar)) {
    const limit = limitlar[toifa];
    if (limit === undefined) {
      continue;
    }
    if (sarf > limit) {
      oshganlar.push({ toifa, sarf, limit, ortiqcha: sarf - limit });
    }
  }
  return oshganlar;
}

console.log(foizHisobla(2_500_000, 4_985_000)); // 50
console.log(foizHisobla(150_000, 0)); // 0
  • (qism * 100) / jami — avval butun songa ko'paytirish, bo'lish oxirida va bitta (Pul darsidagi qoida).
  • Math.round — eng yaqin butun songa: 50,15% → 50% (Math).
  • if (jami === 0) return 0 — nolga bo'lish xato bermaydi, lekin NaN yoki Infinity qaytaradi. Oy boshida xarajat yo'q bo'lsa, hisobotda NaN% chiqmasin.
  • const [toifa, sarf] — Object.entries har juftlikni ["ovqat", 1620000] qilib beradi, massiv destructuring uni ikkiga ajratadi (Massiv destructuring).
  • sarf > limit, >= emas: ijara aynan limitga teng — bu oshish emas (Taqqoslash operatorlari).
js
const oshganlar = oshganToifalar(yigindilar, limitlar);

const [birinchi, ikkinchi] = oshganlar;

console.log(oshganlar.length); // 2
console.log(birinchi.toifa, birinchi.ortiqcha); // ovqat 120000
console.log(ikkinchi.toifa, ikkinchi.ortiqcha); // kafe 100000

Ovqat 120 000, kafe 100 000 so'mga oshgan. Transport va ijara — joyida.

Tekshirib ko'ring: Hamma toifalarning foizlarini qo'shsak, doim aniq 100 chiqadimi?

Javob

Yo'q. Har foiz alohida yaxlitlanadi: uchta teng toifa — har biri 33,33% → 33%, yig'indisi 99%. Ba'zan 101% chiqadi. Bu Pul darsidagi "qismlarni alohida yaxlitlash" tuzog'i. Bizda yig'indi aniq 100 chiqdi — omad. Foiz taxminiy ko'rsatkich, aniq raqam summa ustunida.

8. 5-bosqich: chiroyli hisobot

8.1 So'mni o'qishga qulay qilish

2500000 ni bir qarashda o'qish qiyin: 250 ming? 2,5 million? Hisobotda 2 500 000 kerak. somYoz(son) raqamlarni o'ngdan uchtadan guruhlaydi. G'oya: raqamlarni chapdan o'ngga yuramiz. Har raqamdan oldin tekshiramiz: o'ngida qolgan raqamlar soni 3 ga bo'linadimi? Bo'linsa — bo'sh joy.

js
function somYoz(son) {
  const raqamlar = String(Math.abs(son));
  let natija = "";
  for (let i = 0; i < raqamlar.length; i++) {
    const qolgani = raqamlar.length - i;
    if (i > 0 && qolgani % 3 === 0) {
      natija += " ";
    }
    natija += raqamlar[i];
  }
  return son < 0 ? `-${natija}` : natija;
}

console.log(somYoz(2_515_000)); // 2 515 000
console.log(somYoz(95_000)); // 95 000
console.log(somYoz(500)); // 500
console.log(somYoz(-120_000)); // -120 000

qolgani — joriy raqamning o'zi va undan o'ngdagilar soni. Masalan, "2515000" da 7 ta raqam. Ikkinchi raqam "5" da (i = 1) qolgani = 6, ya'ni "515000". 6 % 3 = 0 — demak "5" dan oldin bo'sh joy. Beshinchi raqamda (i = 4) qolgani = 3 — yana bo'sh joy. Nega i > 0 sharti kerak? "500000" da birinchi raqamning o'zida ham qolgani = 6. Shartsiz satr boshida ortiqcha bo'sh joy paydo bo'lardi. Yordamchilar: String() sonni satrga aylantiradi (Turlarni aniq o'zgartirish). Math.abs minusni vaqtincha olib tashlaydi. % — bo'lishdagi qoldiq (Arifmetik operatorlar).

Maslahat: Real loyihada buni Pul darsidagi Intl.NumberFormat qiladi. Bu yerda mashq uchun o'zimiz yozdik.

8.2 Ustunlarni tekislash

Chekdagi kabi: nom chapda, summa o'ngda. Kesish, bo'lish va to'ldirish darsidagi ikki metod:

  • "ovqat".padEnd(12) — oxiriga bo'sh joy qo'shib, uzunlikni 12 ga yetkazadi: nom chapga tekislanadi.
  • "1 620 000".padStart(16) — boshiga bo'sh joy qo'shadi: son o'ngga tekislanadi.

padStart faqat satrda bor — shuning uchun avval somYoz, keyin padStart. Template literal natijasi ham satr, shuning uchun unga ham padStart qo'yish mumkin. Kenglik: 12 + 24 = 36 — chiziq uzunligi. Jadval qatorida ham 12 + 16 + 8 = 36. hisobQatori bitta "nom ... summa so'm" qatorini qaytaradi. hisobotTuz butun hisobotni qatorlar massivi qilib qaytaradi — ekranga yozmaydi.

js
function hisobQatori(nom, summa) {
  return nom.padEnd(12) + `${somYoz(summa)} so'm`.padStart(24);
}

function hisobotTuz({ oy, daromad, xarajat, yigindilar }) {
  const chiziq = "=".repeat(36);
  const qatorlar = [chiziq, `  OYLIK BYUDJET — ${oy}`, chiziq];
  qatorlar.push(hisobQatori("Daromad:", daromad));
  qatorlar.push(hisobQatori("Xarajat:", xarajat));
  qatorlar.push(hisobQatori("Qoldi:", daromad - xarajat));
  qatorlar.push("-".repeat(36));
  qatorlar.push(
    "Toifa".padEnd(12) + "Summa".padStart(16) + "Ulush".padStart(8),
  );
  for (const [toifa, summa] of Object.entries(yigindilar)) {
    const summaMatn = somYoz(summa).padStart(16);
    const ulush = `${foizHisobla(summa, xarajat)}%`.padStart(8);
    qatorlar.push(toifa.padEnd(12) + summaMatn + ulush);
  }
  return qatorlar;
}

console.log(hisobQatori("Qoldi:", 2_515_000));

Bu blokni ishga tushirsangiz, bitta qator chiqadi — nom chapda, summa o'ngda, jami 36 belgi:

text
Qoldi:                2 515 000 so'm

hisobotTuz to'rtta alohida parametr o'rniga bitta options obyekti oladi: chaqirishda tartibni adashtirib bo'lmaydi, har qiymat nomi bilan beriladi (Parametrda destructuring va options obyekti).

8.3 Jadval uchun ma'lumot

console.table obyektlar massivini jadval qilib chizadi: obyekt — qator, kalit — ustun (console API). Shunday massiv tayyorlaymiz:

js
function jadvalTuz(yigindilar, limitlar) {
  const jadval = [];
  for (const [toifa, sarf] of Object.entries(yigindilar)) {
    const limit = limitlar[toifa] ?? null;
    let holat = "limitsiz";
    if (limit !== null) {
      holat = sarf > limit ? "OSHDI" : "joyida";
    }
    jadval.push({ toifa, sarf, limit, holat });
  }
  return jadval;
}

const jadval = jadvalTuz(yigindilar, limitlar);
console.log(jadval[1].toifa, jadval[1].holat); // ovqat OSHDI

jadval[1] — massivning ikkinchi elementi, "ovqat" qatori. Limiti yo'q toifada limitlar[toifa] — undefined. ?? null uni null ga almashtiradi: "ataylab yo'q" ma'nosi aniqroq (undefined va null). holat — ternary bilan (Ternary).

Bo'sh joyni to'ldiring. "kafe".padEnd(12) qaytargan satr uzunligi — belgi.

9. 6-bosqich: hammasini yig'amiz

"Oshpazlar" tayyor. Endi "ofitsiant" — natijani foydalanuvchiga yetkazadigan yagona funksiya. Hamma yon ta'sir shu yerda:

js
function dasturniIshgaTushir() {
  const { togrilar, xatolar } = xarajatlarniTekshir(xarajatlar);
  for (const x of xatolar) {
    console.warn(`⚠️ O'tkazildi: "${x.izoh}" — ${x.sabab}`);
  }

  const daromad = jamiHisobla(daromadlar);
  const xarajat = jamiHisobla(togrilar);
  const yigindilar = toifalarBoyicha(togrilar);

  const hisobot = hisobotTuz({ oy, daromad, xarajat, yigindilar });
  for (const q of hisobot) {
    console.log(q);
  }

  for (const o of oshganToifalar(yigindilar, limitlar)) {
    console.log(
      `🔴 ${o.toifa}: limit ${somYoz(o.limit)}, ` +
        `${somYoz(o.ortiqcha)} so'm oshdi`,
    );
  }
  console.table(jadvalTuz(yigindilar, limitlar));
}

dasturniIshgaTushir();

Terminalda node byudjet.js ni ishga tushiring. Mana Aziz kompyuterida chiqqan to'liq natija:

text
⚠️ O'tkazildi: "Krossovka" — summa musbat emas
⚠️ O'tkazildi: "JS kitobi" — summa son emas
⚠️ O'tkazildi: "Non" — summa butun son emas
====================================
  OYLIK BYUDJET — 2026-yil oktabr
====================================
Daromad:              7 500 000 so'm
Xarajat:              4 985 000 so'm
Qoldi:                2 515 000 so'm
------------------------------------
Toifa                  Summa   Ulush
ijara              2 500 000     50%
ovqat              1 620 000     32%
transport            275 000      6%
aloqa                150 000      3%
kafe                 400 000      8%
boshqa                40 000      1%
🔴 ovqat: limit 1 500 000, 120 000 so'm oshdi
🔴 kafe: limit 300 000, 100 000 so'm oshdi
┌─────────┬─────────────┬─────────┬─────────┬────────────┐
│ (index) │ toifa       │ sarf    │ limit   │ holat      │
├─────────┼─────────────┼─────────┼─────────┼────────────┤
│ 0       │ 'ijara'     │ 2500000 │ 2500000 │ 'joyida'   │
│ 1       │ 'ovqat'     │ 1620000 │ 1500000 │ 'OSHDI'    │
│ 2       │ 'transport' │ 275000  │ 300000  │ 'joyida'   │
│ 3       │ 'aloqa'     │ 150000  │ 150000  │ 'joyida'   │
│ 4       │ 'kafe'      │ 400000  │ 300000  │ 'OSHDI'    │
│ 5       │ 'boshqa'    │ 40000   │ null    │ 'limitsiz' │
└─────────┴─────────────┴─────────┴─────────┴────────────┘

Natijani o'qing:

  • Uchta buzuq yozuv dasturni to'xtatmadi — ogohlantirish bilan chetga olindi. console.warn Node'da xato oqimiga (stderr) yozadi (console API); terminalda u oddiy qatorlar orasida ko'rinadi.
  • Aziz oy oxirida 2 515 000 so'm bilan qoladi. Pulning yarmi ijaraga ketyapti, ovqat va kafe limitdan oshgan.
  • Jadvalda summalar formatlanmagan: console.table qiymatni o'zgartirmay ko'rsatadi. Matnli hisobot — odam uchun, jadval — tez tekshirish uchun.

dasturniIshgaTushir deyarli gap kabi o'qiladi: tekshir, hisobla, tuz, chiqar. Xato chiqsa, qaysi funksiyaga qarash kerakligi darrov ko'rinadi.

To'liq fayl: byudjet.js (nusxa olish uchun)

Bosqichlardan yig'ilgan fayl. Sinov qatorlari (console.log(xatoSababi(...)) va boshqalar) olib tashlangan.

js
// byudjet.js — oylik byudjet hisoboti (08-qism yakuniy loyihasi)

// 1. Ma'lumot
const oy = "2026-yil oktabr";

const daromadlar = [
  { manba: "Oylik maosh", summa: 6_000_000 },
  { manba: "Frilans: sayt", summa: 1_500_000 },
];

const xarajatlar = [
  { toifa: "ijara", summa: 2_500_000, izoh: "Kvartira" },
  { toifa: "ovqat", summa: 1_200_000, izoh: "Bozor" },
  { toifa: "transport", summa: 180_000, izoh: "Metro, avtobus" },
  { toifa: "ovqat", summa: 420_000, izoh: "Supermarket" },
  { toifa: "aloqa", summa: 150_000, izoh: "Internet, telefon" },
  { toifa: "kafe", summa: 260_000, izoh: "Do'stlar bilan" },
  { toifa: "transport", summa: 95_000, izoh: "Taksi" },
  { toifa: "kafe", summa: 140_000, izoh: "Tug'ilgan kun" },
  { toifa: "kiyim", summa: -350_000, izoh: "Krossovka" },
  { toifa: "kitob", summa: "85000", izoh: "JS kitobi" },
  { summa: 40_000, izoh: "Sartarosh" },
  { toifa: "ovqat", summa: 37_500.5, izoh: "Non" },
];

const limitlar = {
  ijara: 2_500_000,
  ovqat: 1_500_000,
  transport: 300_000,
  aloqa: 150_000,
  kafe: 300_000,
};

// 2. Tekshirish
function xatoSababi(summa) {
  if (typeof summa !== "number") {
    return "summa son emas";
  }
  if (!Number.isInteger(summa)) {
    return "summa butun son emas";
  }
  if (summa <= 0) {
    return "summa musbat emas";
  }
  return null;
}

function xarajatlarniTekshir(royxat) {
  const togrilar = [];
  const xatolar = [];
  for (const yozuv of royxat) {
    const sabab = xatoSababi(yozuv.summa);
    if (sabab !== null) {
      xatolar.push({ ...yozuv, sabab });
      continue;
    }
    togrilar.push({ ...yozuv, toifa: yozuv.toifa ?? "boshqa" });
  }
  return { togrilar, xatolar };
}

// 3. Hisob-kitob
function jamiHisobla(royxat) {
  let jami = 0;
  for (const { summa } of royxat) {
    jami += summa;
  }
  return jami;
}

function toifalarBoyicha(royxat) {
  const yigindilar = {};
  for (const { toifa, summa } of royxat) {
    yigindilar[toifa] = (yigindilar[toifa] ?? 0) + summa;
  }
  return yigindilar;
}

function foizHisobla(qism, jami) {
  if (jami === 0) {
    return 0;
  }
  return Math.round((qism * 100) / jami);
}

function oshganToifalar(yigindilar, limitlar) {
  const oshganlar = [];
  for (const [toifa, sarf] of Object.entries(yigindilar)) {
    const limit = limitlar[toifa];
    if (limit === undefined) {
      continue;
    }
    if (sarf > limit) {
      oshganlar.push({ toifa, sarf, limit, ortiqcha: sarf - limit });
    }
  }
  return oshganlar;
}

// 4. Ko'rinish
function somYoz(son) {
  const raqamlar = String(Math.abs(son));
  let natija = "";
  for (let i = 0; i < raqamlar.length; i++) {
    const qolgani = raqamlar.length - i;
    if (i > 0 && qolgani % 3 === 0) {
      natija += " ";
    }
    natija += raqamlar[i];
  }
  return son < 0 ? `-${natija}` : natija;
}

function hisobQatori(nom, summa) {
  return nom.padEnd(12) + `${somYoz(summa)} so'm`.padStart(24);
}

function hisobotTuz({ oy, daromad, xarajat, yigindilar }) {
  const chiziq = "=".repeat(36);
  const qatorlar = [chiziq, `  OYLIK BYUDJET — ${oy}`, chiziq];
  qatorlar.push(hisobQatori("Daromad:", daromad));
  qatorlar.push(hisobQatori("Xarajat:", xarajat));
  qatorlar.push(hisobQatori("Qoldi:", daromad - xarajat));
  qatorlar.push("-".repeat(36));
  qatorlar.push(
    "Toifa".padEnd(12) + "Summa".padStart(16) + "Ulush".padStart(8),
  );
  for (const [toifa, summa] of Object.entries(yigindilar)) {
    const summaMatn = somYoz(summa).padStart(16);
    const ulush = `${foizHisobla(summa, xarajat)}%`.padStart(8);
    qatorlar.push(toifa.padEnd(12) + summaMatn + ulush);
  }
  return qatorlar;
}

function jadvalTuz(yigindilar, limitlar) {
  const jadval = [];
  for (const [toifa, sarf] of Object.entries(yigindilar)) {
    const limit = limitlar[toifa] ?? null;
    let holat = "limitsiz";
    if (limit !== null) {
      holat = sarf > limit ? "OSHDI" : "joyida";
    }
    jadval.push({ toifa, sarf, limit, holat });
  }
  return jadval;
}

// 5. Ishga tushirish: yon ta'sirlar shu yerda
function dasturniIshgaTushir() {
  const { togrilar, xatolar } = xarajatlarniTekshir(xarajatlar);
  for (const x of xatolar) {
    console.warn(`⚠️ O'tkazildi: "${x.izoh}" — ${x.sabab}`);
  }

  const daromad = jamiHisobla(daromadlar);
  const xarajat = jamiHisobla(togrilar);
  const yigindilar = toifalarBoyicha(togrilar);

  const hisobot = hisobotTuz({ oy, daromad, xarajat, yigindilar });
  for (const q of hisobot) {
    console.log(q);
  }

  for (const o of oshganToifalar(yigindilar, limitlar)) {
    console.log(
      `🔴 ${o.toifa}: limit ${somYoz(o.limit)}, ` +
        `${somYoz(o.ortiqcha)} so'm oshdi`,
    );
  }
  console.table(jadvalTuz(yigindilar, limitlar));
}

dasturniIshgaTushir();

Konsolda:

text
⚠️ O'tkazildi: "Krossovka" — summa musbat emas
⚠️ O'tkazildi: "JS kitobi" — summa son emas
⚠️ O'tkazildi: "Non" — summa butun son emas
====================================
  OYLIK BYUDJET — 2026-yil oktabr
====================================
Daromad:              7 500 000 so'm
Xarajat:              4 985 000 so'm
Qoldi:                2 515 000 so'm
------------------------------------
Toifa                  Summa   Ulush
ijara              2 500 000     50%
ovqat              1 620 000     32%
transport            275 000      6%
aloqa                150 000      3%
kafe                 400 000      8%
boshqa                40 000      1%
🔴 ovqat: limit 1 500 000, 120 000 so'm oshdi
🔴 kafe: limit 300 000, 100 000 so'm oshdi
┌─────────┬─────────────┬─────────┬─────────┬────────────┐
│ (index) │ toifa       │ sarf    │ limit   │ holat      │
├─────────┼─────────────┼─────────┼─────────┼────────────┤
│ 0       │ 'ijara'     │ 2500000 │ 2500000 │ 'joyida'   │
│ 1       │ 'ovqat'     │ 1620000 │ 1500000 │ 'OSHDI'    │
│ 2       │ 'transport' │ 275000  │ 300000  │ 'joyida'   │
│ 3       │ 'aloqa'     │ 150000  │ 150000  │ 'joyida'   │
│ 4       │ 'kafe'      │ 400000  │ 300000  │ 'OSHDI'    │
│ 5       │ 'boshqa'    │ 40000   │ null    │ 'limitsiz' │
└─────────┴─────────────┴─────────┴─────────┴────────────┘

10. Debug: NaN qayerdan chiqdi?

Loyiha birinchi urinishda kamdan-kam ishlaydi — bu normal. Aziz toifalarBoyicha ni birinchi marta shunday yozgan edi:

js
function toifalarBoyicha(royxat) {
  const yigindilar = {};
  for (const { toifa, summa } of royxat) {
    yigindilar[toifa] += summa;
  }
  return yigindilar;
}

const sinov = [
  { toifa: "kafe", summa: 260_000 },
  { toifa: "kafe", summa: 140_000 },
];
console.log(toifalarBoyicha(sinov)); // { kafe: NaN }

Kod xato bermadi, lekin natija noto'g'ri — eng yoqimsiz xato turi. Tartib bilan qidiramiz.

1-qadam: xatoni kichik misolda qayta hosil qiling. 12 ta yozuv emas — ikkita (sinov). Toza funksiyani butun dasturdan ajratib sinash mumkin.

2-qadam: ichini ko'ring. Eng tez yo'l — sikl ichiga vaqtincha console.log:

js
function toifalarBoyicha(royxat) {
  const yigindilar = {};
  for (const { toifa, summa } of royxat) {
    console.log(toifa, yigindilar[toifa], summa); // vaqtincha
    yigindilar[toifa] += summa;
  }
  return yigindilar;
}

toifalarBoyicha([
  { toifa: "kafe", summa: 260_000 },
  { toifa: "kafe", summa: 140_000 },
]);

Konsolda:

text
kafe undefined 260000
kafe NaN 140000

Topildi: birinchi aylanishda yigindilar.kafe — undefined, undefined + 260000 esa NaN (Yashirin coercion). NaN ga nima qo'shsangiz ham — NaN.

3-qadam: debugger bilan. Katta dasturda console.log yozib o'tirish charchatadi. DevTools bilan debug qilish darsidagi ikki yo'l:

  • VS Code: Ctrl+Shift+P → JavaScript Debug Terminal. yigindilar[toifa] += summa; qatori raqamining chap tomonini bosing — qizil nuqta (breakpoint). Terminalda node byudjet.js — dastur shu qatorda to'xtaydi.
  • Chrome: qatordan oldin debugger; yozing va node --inspect-brk byudjet.js ni ishga tushiring. Chrome'da chrome://inspect → Open dedicated DevTools for Node, keyin F8 — dastur debugger da to'xtaydi.

To'xtagach, Watch paneliga yigindilar[toifa] ni qo'shing — undefined. F10 bilan bir qadam yuring — kafe: NaN paydo bo'ladi. Gumon tasdiqlandi.

4-qadam: tuzating va qayta tekshiring. 3-bosqichdagi (yigindilar[toifa] ?? 0) + summa qatori bilan sinov endi { kafe: 400000 } beradi. Vaqtincha console.log va debugger qatorlarini o'chiring — unutilgan debugger dasturni kutilmaganda to'xtatadi.

Tekshirib ko'ring: Nega xatoni butun dasturda emas, ikki yozuvli sinov massivida qidirdik?

Javob

Kichik misolda hamma qiymat ko'rinadi: ikki aylanish, ikki qator. 12 ta yozuvda kerakli qator yo'qolib ketadi.

11. Saqlash va GitHub'ga yuborish

Endi dasturni Git qismida ochilgan mashqlar reposiga qo'shamiz: 08-qism ishlari — 08/ papkasida, loyiha — 08/byudjet/ da.

Git Bash'da:

bash
cd ~/kurs/mashqlar
mkdir -p 08/byudjet

byudjet.js ni 08/byudjet/ ichiga saqlang va commitdan oldin ishlashini tekshiring:

bash
node 08/byudjet/byudjet.js
git status -s

Natija — yuqoridagi hisobot, keyin:

text
?? 08/

?? — Git bu papkani hali kuzatmaydi (Birinchi repo). Commit xabari — mashqlar kelishuvi bo'yicha: avval soha (papka), keyin buyruq maylidagi fe'l (Yaxshi commit):

bash
git add 08/byudjet
git commit -m "08/byudjet: oylik byudjet dasturini qo'sh"
git push

Commit chiqishi:

text
[main ac5ffe3] 08/byudjet: oylik byudjet dasturini qo'sh
 1 file changed, 174 insertions(+)
 create mode 100644 08/byudjet/byudjet.js

Sizda SHA va qatorlar soni boshqacha bo'ladi — bu normal. git push dan keyin GitHub'da login/mashqlar ni oching: 08/byudjet/byudjet.js ko'rinishi kerak (Remote, push va pull).

Diqqat: Ma'lumotni o'zingiznikiga almashtirgan bo'lsangiz, commitdan oldin git diff --staged bilan qarang. Ochiq repoga tushgan narsani tarixdan o'chirish qiyin.

12. Baholash ro'yxati

O'zingiz baholang, yaxshisi — kursdoshingiz: begona ko'z ko'proq narsani ko'radi.

Majburiy shartlar. Bittasi bajarilmasa, loyiha qabul qilinmaydi:

  • node byudjet.js xatosiz ishlaydi va hisobot chiqaradi.
  • Pul faqat butun so'mda hisoblanadi, kasr son yo'q.
  • Dastur ishlagandan keyin asl xarajatlar o'zgarmagan: dasturniIshgaTushir(); dan keyin vaqtincha console.log(xarajatlar[10].toifa); yozing — undefined chiqishi kerak.
  • Fayl mashqlar reposida, 08/byudjet/ papkasida, GitHub'da ko'rinadi.

Ballar (jami 100, o'tish — 80):

Bo'lim Nima tekshiriladi Ball
Ma'lumot Obyektlar massivi, limitlar obyekti, _ ajratgich 10
Tekshirish Son emas, kasr, manfiy — uchalasi ushlanadi, sababi aytiladi 15
Yig'indilar Bitta jamiHisobla ikkala ro'yxatga, toifa hisoblagichi ?? bilan 15
Foiz va limit Butun foiz, nolga bo'lish himoyasi, > to'g'ri tanlangan 10
Hisobot padStart/padEnd bilan tekis ustunlar, so'm guruhlangan 15
Jadval console.table limitsiz toifani ham to'g'ri ko'rsatadi 5
Toza kod Hisob funksiyalari toza, spread bilan nusxa, yon ta'sir bitta joyda 15
Nomlar Har funksiya nomi bitta ishni aytadi, fe'l yoki natija nomi 5
Git To'g'ri papka, kelishuvdagi commit xabari, push qilingan 10

Har bo'limda: hammasi bajarilgan — to'liq ball, bitta kamchilik — yarmi, ko'p kamchilik — 0.

  • 90–100 — a'lo: loyihani portfolio'ga qo'shing («Mashqlar» oxiri).
  • 80–89 — yaxshi: kamchiliklarni tuzating.
  • 80 dan kam — ball yo'qolgan bo'limning tayanch darsiga qayting. Takrorlash bilimni mustahkamlaydi.

13. Ko'p uchraydigan xatolar

13.1 Asl yozuvni o'zgartirib qo'yish

js
const xarajatlar = [{ summa: 40_000, izoh: "Sartarosh" }];

function toifaniTuzat(royxat) {
  const natija = [];
  for (const yozuv of royxat) {
    yozuv.toifa = yozuv.toifa ?? "boshqa"; // ❌ aslini o'zgartiradi
    natija.push(yozuv);
  }
  return natija;
}

toifaniTuzat(xarajatlar);
console.log(xarajatlar[0].toifa); // boshqa

Massiv yangi, lekin ichida asl obyektlar — va ular o'zgardi. Endi asl ma'lumotda "boshqa" turibdi, go'yo Aziz uni o'zi kiritgandek. Tuzatish: natija.push({ ...yozuv, toifa: yozuv.toifa ?? "boshqa" }) — har yozuvning nusxasi.

13.2 Satr summani tekshirmasdan qo'shish

js
let jami = 0;
for (const summa of [2_500_000, "85000", 150_000]) {
  jami += summa;
}
console.log(jami); // 250000085000150000

Xato yo'q, lekin 18 xonali son! 2500000 + "85000" — qo'shish emas, satrlarni yopishtirish. Keyin 150000 ham yopishadi (Yashirin coercion). Shuning uchun tekshirish hisobdan oldin turadi.

13.3 Songa padStart

js
const summa = 2_500_000;
console.log(summa.padStart(12));

Konsolda:

text
TypeError: summa.padStart is not a function

Tarjimasi: "summa.padStart — funksiya emas". Sonda padStart yo'q, u faqat satrda bor. Tuzatish: String(summa).padStart(12) yoki bizdagidek somYoz(summa).padStart(12).

14. Mashqlar

1-mashq (oson): yangi toifa

Aziz sport zaliga yozildi. xarajatlar ga { toifa: "sport", summa: 120_000, izoh: "Zal" } ni, limitlar ga sport: 100_000 ni qo'shing. Ishga tushirishdan oldin ayting: hisobotda nima o'zgaradi? Keyin tekshiring.

Ishora: massivga yozuv — vergul bilan yangi qator, obyektga kalit — sport: 100_000, (Obyekt). O'ylab ko'ring: jami xarajat o'zgarsa, boshqa toifalarning foizlariga nima bo'ladi?

Yechim

Kod o'zgarmaydi — faqat ma'lumot. Hisobotning o'zgargan qismi:

text
Xarajat:              5 105 000 so'm
Qoldi:                2 395 000 so'm
------------------------------------
Toifa                  Summa   Ulush
ijara              2 500 000     49%
ovqat              1 620 000     32%
transport            275 000      5%
aloqa                150 000      3%
kafe                 400 000      8%
boshqa                40 000      1%
sport                120 000      2%
🔴 ovqat: limit 1 500 000, 120 000 so'm oshdi
🔴 kafe: limit 300 000, 100 000 so'm oshdi
🔴 sport: limit 100 000, 20 000 so'm oshdi

Jami o'zgargani uchun boshqa foizlar ham o'zgardi: ijara 50% → 49%, transport 6% → 5%. Yangi toifa oxirida chiqdi: Object.entries kalitlarni qo'shilgan tartibda beradi (Obyekt bo'ylab yurish). Kod o'zgarmasdan yangi toifa bilan ishladi — yaxshi tuzilgan dastur belgisi.

2-mashq (o'rta): jamg'arma foizi

jamgarmaFoizi(daromad, xarajat) funksiyasini yozing: daromadning necha foizi qolganini butun son bilan qaytarsin. Keyin jamgarmaXabari(foiz) — foiz 20 dan kam bo'lsa "⚠️ Jamg'arma 15% — 20% dan kam", aks holda "✅ Jamg'arma 34%" qaytarsin.

Ishora: tayyor foizHisobla ni qayta ishlating — qolgan pul daromad - xarajat. Matn — template literal (Template literals).

Yechim
js
function foizHisobla(qism, jami) {
  if (jami === 0) {
    return 0;
  }
  return Math.round((qism * 100) / jami);
}

function jamgarmaFoizi(daromad, xarajat) {
  return foizHisobla(daromad - xarajat, daromad);
}

function jamgarmaXabari(foiz) {
  if (foiz < 20) {
    return `⚠️ Jamg'arma ${foiz}% — 20% dan kam`;
  }
  return `✅ Jamg'arma ${foiz}%`;
}

const foiz = jamgarmaFoizi(7_500_000, 4_985_000);
console.log(foiz); // 34
console.log(jamgarmaXabari(foiz)); // ✅ Jamg'arma 34%
console.log(jamgarmaXabari(jamgarmaFoizi(7_500_000, 6_400_000)));

Konsolda:

text
34
✅ Jamg'arma 34%
⚠️ Jamg'arma 15% — 20% dan kam

Ikkala funksiya toza. dasturniIshgaTushir ga bitta qator qo'shiladi: console.log(jamgarmaXabari(jamgarmaFoizi(daromad, xarajat)));. Xarajat daromaddan oshsa, foiz manfiy — u ham 20 dan kam, ogohlantirish chiqadi.

3-mashq (qiyin): "Agar ... bo'lsa?"

Aziz 3 200 000 so'mlik noutbuk olmoqchi. Shu oy olsa, qancha qoladi? Asl ro'yxatga tegmasdan, xarid qo'shilgan yangi ro'yxat bilan hisoblang.

xarajatQosh(royxat, yangi) ni yozing: yozuv to'g'ri bo'lsa (xatoSababi bo'yicha) — yangi massiv, noto'g'ri bo'lsa — ro'yxatning nusxasi. Ikki holatda qolgan pulni chiqaring va asl massiv uzunligi o'zgarmaganini ko'rsating.

Ishora: massivga yozuvni spread bilan qo'shish — [...royxat, yangi] (Spread va rest).

Yechim
js
function xatoSababi(summa) {
  if (typeof summa !== "number") {
    return "summa son emas";
  }
  if (!Number.isInteger(summa)) {
    return "summa butun son emas";
  }
  if (summa <= 0) {
    return "summa musbat emas";
  }
  return null;
}

function jamiHisobla(royxat) {
  let jami = 0;
  for (const { summa } of royxat) {
    jami += summa;
  }
  return jami;
}

function xarajatQosh(royxat, yangi) {
  if (xatoSababi(yangi.summa) !== null) {
    return [...royxat];
  }
  return [...royxat, yangi];
}

const daromad = 7_500_000;
const xarajatlar = [
  { toifa: "ijara", summa: 2_500_000 },
  { toifa: "ovqat", summa: 1_620_000 },
];
const noutbuk = { toifa: "texnika", summa: 3_200_000 };

const reja = xarajatQosh(xarajatlar, noutbuk);
console.log(daromad - jamiHisobla(xarajatlar)); // 3380000
console.log(daromad - jamiHisobla(reja)); // 180000
console.log(xarajatlar.length, reja.length); // 2 3

Noutbuk olinsa, atigi 180 000 so'm qoladi — xaridni keyingi oyga qoldirgan ma'qul. xarajatlar.length hali ham 2: [...royxat, yangi] yangi massiv yaratdi, asliga push qilinmadi. Noto'g'ri yozuvda ham [...royxat] — funksiya doim yangi massiv qaytaradi.

4-mashq: Portfolio qadami — loyiha kartasi

Byudjet — birinchi to'liq dasturingiz. Uni portfolio'dagi "Loyihalar" sahifasiga qo'shing.

  1. kurs/portfolio da sayt/loyihalar/index.html ni oching.
  2. <div class="loyihalar"> ichiga, mavjud kartalar qatoriga yangi article qo'shing. Tuzilmasi boshqa kartalardagidek bo'lsin: h2, time, tavsif va havola (Yakuniy loyiha: portfolio).
  3. Havola — GitHub'dagi 08/byudjet papkasiga.
  4. npm run check bilan tekshiring va commit qiling: Loyihalar: byudjet dasturi kartasini qo'sh. git push dan keyin sayt GitHub Actions orqali o'zi yangilanadi (Avtomatik deploy).
Yechim
html
<article>
  <h2>Oylik byudjet — konsol dasturi</h2>
  <p><time datetime="2026-10">2026-yil oktabr</time></p>
  <p>
    Node.js dasturi: xarajatlarni tekshiradi, toifa bo'yicha
    yig'indi va ulushni hisoblaydi, limitdan oshganda ogohlantiradi.
  </p>
  <ul>
    <li>
      <a
        href="https://github.com/login/mashqlar/tree/main/08/byudjet"
      >Kodni GitHub'da ko'rish</a>
    </li>
  </ul>
</article>

login — GitHub nomingiz. Boshqa kartalarda ul ga role="list" qo'shgan bo'lsangiz, bunga ham qo'shing. .loyihalar > article selektori yangi kartani ham o'zi bezaydi. Rasm o'rniga keyinroq terminal skrinshotini qo'yishingiz mumkin.

15. Real ishda

  • Hisobotlar hamma joyda. Bank ilovasidagi "xarajatlar tahlili", do'konning kunlik savdo hisoboti — hammasi shu sxema: ma'lumot → tekshirish → guruhlash → foiz → chiqarish. Farqi: ma'lumot bazadan keladi, natija veb-sahifada chiziladi.
  • Ma'lumotni tekshirish — backend dasturchining kundalik ishi. Foydalanuvchi formaga istagan narsani yozadi, server esa hisobdan oldin uni tekshiradi. Bu xavfsizlik ham: tekshirilmagan qiymat hisobni buzadi.
  • Avtomatik testlar. Bugun har bosqichni console.log bilan qo'lda tekshirdingiz. Real loyihada bu tekshiruvlar avtomatik test bo'lib, bitta buyruq bilan ishlaydi. Testlarni kursda keyinroq o'rganamiz — hozir bilish shart emas. Toza funksiya esa testga eng qulay.
  • Intervyuda "buyurtmalarni toifa bo'yicha guruhlang" — odatiy junior savoli.

08-qism yakuni

Tabriklaymiz — "JavaScript asoslari" qismi tugadi! Bugun siz dastur yozdingiz: ma'lumotni qabul qiladi, tekshiradi, hisoblaydi va odam tushunadigan hisobot beradi. Qism boshida bitta console.log yozgan edingiz — endi GitHub'da 170 qatorga yaqin ishlaydigan loyiha turibdi.

Dasturning bitta kamchiligi bor: u faqat terminalda yashaydi. Keyingi qismda JavaScript'ni brauzerga olib chiqamiz: tugma, forma, ekranda chiziladigan ro'yxat. Obyektlar massivi, toza funksiya va spread u yerda har qadamda kerak bo'ladi.

Xulosa

  • Katta vazifa kichik funksiyalarga bo'linadi: har biri bitta ish, nomi shu ishni aytadi.
  • Hisob-kitob — toza funksiyalarda, console — bitta "ofitsiant" funksiyada.
  • Ma'lumot hisobdan oldin tekshiriladi: typeof, Number.isInteger, musbatlik; buzuq yozuv ogohlantirish bilan o'tkaziladi.
  • Obyekt-hisoblagich: (obyekt[kalit] ?? 0) + qiymat.
  • Pul — butun so'mda, foiz — Math.round((qism * 100) / jami), nolga bo'lish himoyalangan.
  • Asl ma'lumot spread bilan himoyalanadi: { ...yozuv }, [...royxat, yangi].

Keyingi dars: Brauzer muhiti: window, document va skript yuklanishi — 09-qism boshlanadi: JavaScript sahifaga qanday ulanishini va brauzer unga qanday imkoniyatlar berishini ko'ramiz.

Manbalar

  • MDN: "String.prototype.padStart()", "String.prototype.padEnd()" — developer.mozilla.org
  • MDN: "Number.isInteger()", "Nullish coalescing operator (??)" — developer.mozilla.org
  • Node.js: "console.table()" — nodejs.org/api/console.html
  • Node.js: "Debugging Node.js" — nodejs.org/en/learn/getting-started/debugging
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript yakuniy loyiha: konsolda oylik byudjet dasturi — IlmHamroh