IlmHamroh
JavaScript Full-stack/12-qism. JavaScript: ilg'or mavzular va kod sifati34/44-dars21 daqiqa
Mundarija (34)

Toza kod: funksiyalar, izohlar va tuzilma — bitta ish, erta qaytish va "nega" izohi

Qisqacha: Toza funksiya bitta ish qiladi va nomi shu ishni aytadi. Shartlar ichma-ich emas — erta qaytish (guard clause) bilan yoziladi. Parametrlar kam (ko'p bo'lsa — sozlama obyekti), true/false bilan xulqni almashtiruvchi "flag argument" yo'q. Sehrli raqam o'rniga nomli o'zgarmas (KUTISH_MS). Izoh kod nima qilishini takrorlamaydi — nega shunday yozilganini tushuntiradi.

Bu darsda

  • Uzun funksiyani "bitta ish — bitta funksiya" qoidasi bilan bo'la olasiz.
  • Ichma-ich shartlarni erta qaytish bilan tekislay olasiz.
  • Parametrlar ko'payganda, flag argument va yashirin yon ta'sirda nima qilishni bilasiz.
  • "Nima" izohini o'chirib, "nega" izohini yoza olasiz; izoh turlarini farqlaysiz.
  • vazifalar ning eng uzun funksiyalarini xulqni o'zgartirmasdan bo'lasiz.

Oldin bilishingiz kerak: Toza kod: nomlash, return va erta qaytish, Pure funksiya va side effect, Parametrda destructuring va options obyekti.

1. Nega bu kerak?

O'tgan darsda nomlarni tozaladik. Endi Sardorga yangi topshiriq: "Server bilan sinxron paytida, agar server ro'yxatni bilmasa, ekranda e'lon chiqsin". U asosiy.js dagi sinxronla() funksiyasini ochadi. Funksiya 40 qatordan iborat: kutilayotgan yozuvlarni tekshiradi, eski so'rovni bekor qiladi, internetni tekshiradi, serverdan ro'yxat oladi, kerak bo'lsa yuboradi, javobni tekshiradi, solishtiradi, holatni yozadi, xatolarni ushlaydi.

Kerakli joyni topish uchun Sardor hammasini o'qib chiqishi kerak. Bitta joyni o'zgartirsa, qo'shni ishni buzib qo'ymaydimi — bilmaydi.

Uzun funksiya — sarlavhasiz, paragrafsiz o'n sahifali bob kabi: ichida hamma narsa bor, lekin hech narsani topib bo'lmaydi. Bu darsda funksiyani nomli "bob"larga bo'lamiz, keyin izohlarga o'tamiz.

2. Funksiya bitta ish qiladi

2.1 Bitta ish nima?

«Bahor»da buyurtma yopilganda chek chiqadi. O'tgan darsda kelishgan edik: bundan keyingi mustaqil misollarda nomlar inglizcha, izohlar o'zbekcha. closeOrder — "buyurtmani yop", order — buyurtma, item — undagi bitta qator, price — narx, qty (quantity) — soni. Sardorning birinchi varianti:

js
function closeOrder(order) {
  let total = 0;
  for (const item of order) {
    total += item.price * item.qty;
  }
  if (total >= 200000) {
    total = (total * 90) / 100;
  }
  console.log("— «Bahor» —");
  for (const item of order) {
    console.log(`${item.name} × ${item.qty}`);
  }
  console.log(`Jami: ${total} so'm`);
}

closeOrder([
  { name: "Osh", price: 35000, qty: 4 },
  { name: "Manti", price: 30000, qty: 2 },
  { name: "Ko'k choy", price: 5000, qty: 4 },
]);

Konsolda:

text
— «Bahor» —
Osh × 4
Manti × 2
Ko'k choy × 4
Jami: 198000 so'm

Bu funksiya nima qiladi? Javobni "va" bilan aytishga to'g'ri keladi: summani hisoblaydi va chegirma beradi va chek chiqaradi. Har "va" — alohida ish. Oddiy sinov shu: funksiya ishini bitta gapda, "va"siz ayta olmasangiz — u bir nechta ish qilyapti.

Endi har ishni o'z funksiyasiga ajratamiz: calculateTotal — "jamini hisobla", applyDiscount — "chegirmani qo'lla", printReceipt — "chekni chiqar". Nomlar o'zi aytib turibdi — izoh shart emas:

js
const DISCOUNT_THRESHOLD = 200000; // so'm
const DISCOUNT_PERCENT = 10;

function calculateTotal(order) {
  return order.reduce(
    (total, item) => total + item.price * item.qty,
    0,
  );
}

function applyDiscount(amount) {
  if (amount < DISCOUNT_THRESHOLD) {
    return amount;
  }
  return (amount * (100 - DISCOUNT_PERCENT)) / 100;
}

function printReceipt(order, amount) {
  console.log("— «Bahor» —");
  for (const item of order) {
    console.log(`${item.name} × ${item.qty}`);
  }
  console.log(`Jami: ${amount} so'm`);
}

function closeOrder(order) {
  const amount = applyDiscount(calculateTotal(order));
  printReceipt(order, amount);
}

closeOrder([
  { name: "Osh", price: 35000, qty: 4 },
  { name: "Manti", price: 30000, qty: 2 },
  { name: "Ko'k choy", price: 5000, qty: 4 },
]);

Konsolda:

text
— «Bahor» —
Osh × 4
Manti × 2
Ko'k choy × 4
Jami: 198000 so'm

Natija bir xil — xulq o'zgarmadi. Lekin endi closeOrder ni o'qisangiz, u mundarija kabi: "summani hisobla, chegirma ber, chek chiqar". Tafsilot kerak bo'lsa — tegishli funksiyaga kirasiz. Jasur aka "chegirma 15% bo'lsin" desa, Sardor faqat DISCOUNT_PERCENT ni o'zgartiradi va chek kodiga tegmaydi.

Yana bir yutuq: calculateTotal va applyDiscount — toza funksiyalar (Pure funksiya va side effect). Ular ekranga hech narsa yozmaydi, faqat qiymat qaytaradi. Bunday funksiyani tekshirish qulay — keyingi darsda aynan shularga birinchi avtomatik testni yozamiz.

2.2 Bir daraja abstraksiya

Ikkinchi variantdagi closeOrder ichida faqat "katta" qadamlar bor. Unda item.price * item.qty kabi mayda hisob yo'q. Buni bir daraja abstraksiya deyishadi. Abstraksiya — tafsilotni yashirib, faqat "nima qilinadi"ni ko'rsatish; "bir daraja" — funksiya ichidagi hamma qatorlar bir xil "balandlikda" gapiradi. Rahbar "hisobotni tayyorla, Jasur akaga yubor" deydi — u Excel formulasini aytmaydi.

Funksiya qancha uzun bo'lishi kerak? Aniq raqam yo'q. Amaliy mo'ljal: ekranga sig'sin (taxminan 20 qatorgacha) va ishini bir gapda ayta olasiz. vazifalar dagi sinxronla() 40 qator edi — Vazifalar qadamida uni bo'lamiz.

Tekshirib ko'ring: function saveBookingAndNotify(booking) ("bronni saqla va xabar yubor") — nomning o'zi nima haqida signal beradi?

Javob

Nomda "va" (And) bor — demak, funksiya ikki ish qiladi. Ularni ajrating: saveBooking(booking) va notifyGuest(booking), chaqiruvchi esa ikkalasini ketma-ket chaqiradi. Shunda saqlashni xabarsiz ham ishlatish mumkin bo'ladi (masalan, import paytida).

3. Erta qaytish

3.1 Ichma-ich shartlar

return va erta qaytish darsida ko'rgan edik: return funksiyani darhol tugatadi. Bron formasini tekshiradigan funksiyani ikki xil yozamiz. bookingError — "bron xatosi": xato matnini yoki null (xato yo'q) qaytaradi; guests — mehmonlar soni, hour — soat. Avval ichma-ich:

js
const MAX_GUESTS = 20;
const BOOKING_START = 10; // soat
const BOOKING_END = 22;

function bookingError(booking) {
  if (booking.guests >= 1) {
    if (booking.guests <= MAX_GUESTS) {
      if (
        booking.hour >= BOOKING_START &&
        booking.hour <= BOOKING_END
      ) {
        return null;
      } else {
        return "Bron 10:00–22:00 oralig'ida";
      }
    } else {
      return `Ko'pi bilan ${MAX_GUESTS} kishi`;
    }
  } else {
    return "Kamida 1 kishi";
  }
}

console.log(bookingError({ guests: 25, hour: 12 }));

Konsolda:

text
Ko'pi bilan 20 kishi

Kod o'ngga qarab "o'q uchi" shaklida o'sadi. Har else o'zining if idan uzoqda: "Kamida 1 kishi" qaysi shartga tegishli ekanini topish uchun ko'z yuqoriga qaytadi.

3.2 Avval xatolar, keyin asosiy yo'l

Endi har xato holatni boshida tekshirib, darhol qaytamiz. Bunday tekshiruv himoya sharti (guard clause) deyiladi — eshik oldidagi qo'riqchi kabi: mos kelmaganni ichkariga kiritmaydi.

js
const MAX_GUESTS = 20;
const BOOKING_START = 10; // soat
const BOOKING_END = 22;

function bookingError(booking) {
  if (booking.guests < 1) {
    return "Kamida 1 kishi";
  }
  if (booking.guests > MAX_GUESTS) {
    return `Ko'pi bilan ${MAX_GUESTS} kishi`;
  }
  if (booking.hour < BOOKING_START || booking.hour > BOOKING_END) {
    return "Bron 10:00–22:00 oralig'ida";
  }
  return null;
}

console.log(bookingError({ guests: 25, hour: 12 }));
console.log(bookingError({ guests: 4, hour: 19 }));

Konsolda:

text
Ko'pi bilan 20 kishi
null

Har shart va uning javobi yonma-yon. else yo'q, chekinish bir pog'ona. Funksiyaning oxirgi qatori — "hammasi joyida" yo'li. Quyidagi vizualda ikkala chaqiruv qadamma-qadam bajariladi — qaysi return funksiyani tugatganiga qarang:

vazifalar da bu usul hamma joyda bor. Masalan, sinxronla() boshida: if (kutilmoqda > 0) { return; } va if (!navigator.onLine) { … return; }.

Tekshirib ko'ring: Himoya shartlarining tartibi muhimmi? bookingError({ guests: 0, hour: 5 }) da ikki xato bor — qaysi biri qaytadi?

Javob

Muhim. Birinchi mos kelgan shart g'olib: "Kamida 1 kishi" qaytadi, soat tekshirilmaydi ham. Shuning uchun eng asosiy (yoki foydalanuvchi uchun eng tushunarli) xatoni birinchi qo'ying. Hamma xatolarni birdaniga ko'rsatish kerak bo'lsa — qaytarish o'rniga ularni massivga yig'ish kerak.

4. Parametrlar va yon ta'sir

4.1 Parametrlar soni

Parametr qancha ko'p bo'lsa, chaqiruvni o'qish shuncha qiyin (createBooking — "bron yarat"):

js
createBooking("Dilshod aka", 4, "2026-10-05", 19, true, false);

true nima? false nima? Tartibni adashtirsangiz, xato jimgina o'tib ketadi. Mo'ljal: 0–2 parametr — ideal, 3 — chidasa bo'ladi. Undan ko'p bo'lsa, options obyekti ishlating — har qiymatning nomi bor, tartib ahamiyatsiz:

js
createBooking({
  guest: "Dilshod aka",
  guests: 4,
  date: "2026-10-05",
  hour: 19,
  onTerrace: true, // ayvonda
});

vazifalar da shunday: apiKlient({ asosiyUrl, kalit, muvaffaqiyatsiz }).

4.2 Flag argument

Flag argument — funksiyaning ish turini almashtiradigan true/false parametr:

js
printReceipt(order, amount, true); // true — qisqa chekmi?

Chaqiruvni o'qiganda true hech narsa aytmaydi. Aslida bu funksiya ichida ikki funksiya yashiringan: if (short) { … } else { … }. Ularni ajrating — printShortReceipt(order) ("qisqa chekni chiqar") va printReceipt(order, amount). Har birining nomi o'z ishini aytadi.

Sozlama obyekti ichidagi nomli mantiqiy qiymat esa boshqa gap: apiKlient({ muvaffaqiyatsiz: true }) da nom ko'rinib turibdi va u funksiyani ikkiga bo'lmaydi — faqat manzilga parametr qo'shadi.

4.3 Yashirin yon ta'sir

Yon ta'sir (side effect) — funksiya qiymat qaytarishdan tashqari qiladigan ish: faylga yozish, ekranni o'zgartirish, global o'zgaruvchiga tegish. Yon ta'sirning o'zi yomon emas — usiz dastur hech narsa qilmaydi. Yomoni — yashirin yon ta'sir: validate(booking) ("tekshir") nomli funksiya tekshirish bilan birga bronni saqlab ham qo'ysa.

Foydali qoida: funksiya yo so'raydi (qiymat qaytaradi, hech narsani o'zgartirmaydi), yo buyuradi (o'zgartiradi). Ikkalasini birga emas. Bu qoida "buyruq va so'rovni ajratish" (command-query separation) deyiladi. holat.js aynan shunday tuzilgan:

js
// So'rovlar: faqat qiymat qaytaradi
export function xatoniTop(matn, vazifalar, ozId) { … }
export function korinadiganlar() { … }

// O'zgartiruvchilar
export function vazifaQosh(matn) { … }
export function vazifaniOchir(id) { … }

Tekshirib ko'ring: function checkBooking(booking) ("bronni tekshir") xato bo'lsa false qaytaradi, xato bo'lmasa bronni bazaga yozib true qaytaradi. Nima muammo?

Javob

Nom "tekshir" deydi, lekin funksiya yozadi ham — yashirin yon ta'sir. Kimdir formani "faqat tekshirib ko'rish" uchun chaqirsa, bron bazaga tushib qoladi. Ikkiga bo'ling: bookingError(booking) (so'rov) va saveBooking(booking) (buyruq).

5. Sehrli raqamlar va satrlar

Kodda izohsiz turgan 8000, 0.9, "tahrir-maydon" — sehrli qiymat (magic number). U qayerdan kelgani va nimani anglatishi noma'lum. api.js ning birinchi qatorlariga qarang:

js
// api.js — server bilan gaplashadigan yagona joy
const KUTISH_MS = 8000;

8000 o'rniga KUTISH_MS — "kutish, millisekundda": birlik nomning o'zida (_MS), qiymat o'zgarsa — bitta joyda o'zgaradi.

vazifa.js da yana bir yaxshi misol bor:

js
// index.html dagi maxlength bilan bir xil bo'lsin
static MAKS_UZUNLIK = 100;

Bu yerdagi izoh negani aytadi: raqam HTML'dagi boshqa raqam bilan bog'langan, biri o'zgarsa — ikkinchisi ham.

vazifalar da sehrli qiymatlar hali qolgan: "tahrir-maydon" satri ikki modulda olti marta takrorlanadi, eksport paketining versiyasi 1 uch joyda yozilgan. Ularni Code smell va refactoring darsida "kod hidi" sifatida topib tuzatamiz.

Hamma raqam ham sehrli emas. 0 va 1 (reduce boshlang'ich qiymati, indeks), 100 foizda, 2 "ikki baravar" ma'nosida — o'zi tushunarli. Savol bering: "Bu raqamni ko'rgan odam uning ma'nosini so'raydimi?" So'rasa — nom bering.

6. Izohlar

6.1 Izoh turlari

Tur Misol Kerakmi
"Nima" izohi // massivni aylanamiz yo'q — kod o'zi aytadi
"Nega" izohi // Matn oldindan olinadi: o'chirilgandan keyin vazifa yo'q ha — koddan ko'rinmaydi
Ogohlantirish // index.html dagi maxlength bilan bir xil bo'lsin ha
TODO // TODO: bron 30 kundan keyinga bo'lmasin qisqa muddatga
Hujjat izohi (JSDoc) /** @param {string} matn */ eksport qilingan funksiyada

Kod nima qilishini kodning o'zi aytadi — nomlar va tuzilma orqali. Kod nega aynan shunday yozilganini esa aytolmaydi: qaysi xato bo'lgani, qaysi muqobil sinab ko'rilib rad etilgani, qaysi chegara hisobga olingani. Izohning joyi — shu yerda.

Hujjat izohi — /** … */ ichida funksiyaning parametrlari va natijasini maxsus belgilar (@param, @returns) bilan yozish. Uni muharrir o'qiydi va funksiyani chaqirganda maslahat ko'rsatadi. Buni JSDoc va // @ts-check darsida to'liq o'rganamiz, hozir bilish shart emas.

6.2 "Nima" izohi — o'chiriladi

asosiy.js da shunday qatorlar bor edi:

js
// Internet uzildi yoki qaytdi
addEventListener("offline", () => { … });
addEventListener("online", () => sinxronla());

// Boshqa tablardagi o'zgarishlar
tingla(boshqaTabdanYangila);

"offline" va "online" — hodisa nomlari "internet uzildi yoki qaytdi" deb turibdi. boshqaTabdanYangila nomi ham o'zini tushuntiradi. Izoh shunchaki takrorlaydi. Takror izoh zararli: kod o'zgarsa, izoh o'zgarmay qoladi va yolg'on gapira boshlaydi.

render.js da esa 09-qismdan o'quv izohi qolib ketgan edi: // querySelector — royxat'ning metodi: uni nuqta bilan chaqiramiz. Dars uchun foydali, ishlaydigan loyihada — ortiqcha.

6.3 "Nega" izohi — qo'shiladi

render.js dagi kichik funksiya:

js
// Bir xil matnni qayta yozish ham DOM o'zgarishi: jonli hudud
// (#hisob, #aloqa) uni ekran o'quvchiga qayta o'qib berishi mumkin
function matnniYoz(element, matn) {
  if (element.textContent !== matn) {
    element.textContent = matn;
  }
}

Izohsiz Sardor o'ylardi: "Bu if ortiqcha, to'g'ridan-to'g'ri yozib qo'yaman" — va Malika (ko'zi ojiz mehmon, NVDA bilan ishlaydi) har chizishda bir xil e'lonni qayta-qayta eshitardi. Izoh kelajakdagi "tozalovchi"ni xatodan saqlaydi.

6.4 Izohlangan kod

js
// const doneTasks = tasks.filter((t) => t.done);
// console.log(doneTasks);

Bunday "o'chirilgan, lekin qoldirilgan" kodni o'chiring. Uni hech kim qayta yoqmaydi, lekin har o'quvchi "bu nega turibdi, muhimmi?" deb to'xtaydi. Eski kod kerak bo'lsa — git eslab qoladi (Tarixni o'qish: git log).

Tekshirib ko'ring: Quyidagi izoh "nima"mi yoki "nega"mi? // Qiymat bosilgan paytda olinadi: serverga aynan shu yuboriladi

Javob

"Nega". U const bajarildi = !vazifaniTop(id).bajarildi; qatori nega serverdaBajar dan oldin turganini tushuntiradi: ekrandagi o'zgarishdan keyin olinsa, qiymat teskari bo'lib qolardi. Koddan buni bilib bo'lmaydi.

7. Fayl tuzilmasi va formatlash

7.1 Fayl — gazeta kabi

Gazetada sarlavha tepada, tafsilot pastda. Fayl ham shunday o'qilsin: yuqorida importlar va o'zgarmaslar, keyin yordamchi funksiyalar, keyin asosiy (eksport qilinadigan) funksiyalar. asosiy.js da har bo'lim boshida qisqa izoh-sarlavha bor: // Hodisalar: har joyda bitta tinglovchi.

Bog'liq qatorlar birga turadi, bo'sh qator — "paragraf" chegarasi. Bitta funksiya ichida ham: tayyorlash, asosiy ish, natija — orasida bo'sh qator.

7.2 Formatlash — mashinaga

Chekinish 2 bo'sh joymi yoki 4? Bu savolda to'g'ri javob yo'q — muhimi, butun loyiha bir xil bo'lsin. Jamoalar bu bahsni vositaga topshiradi: Prettier kodni avtomatik formatlaydi (ESLint va Prettier darsida vazifalar ga o'rnatamiz). Ungacha: atrofdagi kod qanday yozilgan bo'lsa, shunday yozing.

Funksiya o'lchami haqidagi qoidalar ortida uchta mashhur tamoyil turadi: DRY (takrorlama), KISS (sodda qil), YAGNI (hozir kerak bo'lmagan narsani yozma). Ularni arxitektura qismida (Asosiy tamoyillar) batafsil ko'ramiz, hozir bilish shart emas.

8. Ko'p uchraydigan xatolar

8.1 Haddan tashqari mayda bo'lish

Har qatorni funksiyaga aylantirish ham xato: function addOne(n) { return n + 1; }. O'quvchi o'nlab fayl va funksiyalar orasida sakrab yuradi. Funksiya nomli fikr bo'lsin, bitta qatorning boshqa nomi emas.

8.2 Izoh — yomon kodni oqlash uchun

// x — foydalanuvchining yoshi izohini yozishdan oldin x ni age ga o'zgartiring. Izoh nomlash xatosini yamaydi, tuzatmaydi.

8.3 Eskirgan izoh

Kod o'zgardi, izoh qoldi: // 10% chegirma yonida endi DISCOUNT_PERCENT = 15. Kodni o'zgartirganda uning ustidagi izohni ham o'qing.

8.4 Ajratishda tartibni o'zgartirish

Funksiyani bo'lganda chaqiruvlar tartibi o'zgarib qolsa, xulq ham o'zgaradi. katakniAlmashtir da bajarildi qiymati ekrandagi o'zgarishdan oldin olinadi; keyinga o'tkazilsa — serverga teskari qiymat ketadi. Refaktorda har qatorni "ko'chiraman", "o'zgartirmayman" deb o'ylang.

9. Mashqlar

1-mashq (oson): "Nima" va "nega"

Qaysi izoh qoladi, qaysi biri o'chadi? Nega?

js
// Narxni soniga ko'paytiramiz
const amount = price * qty;

// Mehmon narxni telefonda ko'radi: brauzer "75,000" chiqarishi
// mumkin, shuning uchun o'z formatimiz
const text = formatSom(amount);

// i ni oshiramiz
i++;
Yechim

Birinchi va uchinchi izoh — "nima": kod o'zi aytib turibdi, o'chiriladi. Ikkinchisi — "nega": u Intl.NumberFormat darsidagi Chrome muammosini eslatadi (formatSom — o'sha darsdagi somYoz ning inglizcha nomi) va kelajakdagi o'quvchini toLocaleString ga "soddalashtirish"dan saqlaydi. U qoladi.

2-mashq (o'rta): Erta qaytish

Quyidagi funksiyani himoya shartlari bilan qayta yozing. Natija o'zgarmasin. orderError — "buyurtma xatosi", dish — taom, dish.available — taom bugun bormi, dish.stock — nechta porsiya qolgani.

js
function orderError(dish, qty) {
  if (dish !== undefined) {
    if (dish.available) {
      if (qty <= dish.stock) {
        return null;
      } else {
        return `Faqat ${dish.stock} porsiya qoldi`;
      }
    } else {
      return "Bugun yo'q";
    }
  } else {
    return "Bunday taom yo'q";
  }
}

const manti = { name: "Manti", available: true, stock: 3 };
console.log(orderError(manti, 5)); // Faqat 3 porsiya qoldi
console.log(orderError(undefined, 1)); // Bunday taom yo'q
console.log(orderError(manti, 2)); // null
Yechim
js
function orderError(dish, qty) {
  if (dish === undefined) {
    return "Bunday taom yo'q";
  }
  if (!dish.available) {
    return "Bugun yo'q";
  }
  if (qty > dish.stock) {
    return `Faqat ${dish.stock} porsiya qoldi`;
  }
  return null;
}

const manti = { name: "Manti", available: true, stock: 3 };
console.log(orderError(manti, 5)); // Faqat 3 porsiya qoldi
console.log(orderError(undefined, 1)); // Bunday taom yo'q
console.log(orderError(manti, 2)); // null

Har shartni teskarisiga aylantirdik: dish !== undefined → dish === undefined, qty <= dish.stock → qty > dish.stock. Teskari shartda <= ning juftini adashtirmang: <= ning teskarisi >, >= emas.

3-mashq (qiyin): Bo'ling va flag'dan qutuling

Funksiya ikki ish qiladi va flag argument oladi. report — hisobot, orders — buyurtmalar, short — qisqami, revenue — tushum. Uni uchta funksiyaga bo'ling: kunlik tushumni hisoblash (toza funksiya), to'liq hisobot va qisqa hisobot. Konsol chiqishi o'zgarmasin.

js
function report(orders, short) {
  let revenue = 0;
  for (const order of orders) {
    revenue += order.amount;
  }
  if (short) {
    console.log(`Tushum: ${revenue}`);
  } else {
    console.log(`Buyurtmalar: ${orders.length} ta`);
    console.log(`Tushum: ${revenue} so'm`);
  }
}

const today = [{ amount: 70000 }, { amount: 198000 }];
report(today, true);
report(today, false);

Konsolda:

text
Tushum: 268000
Buyurtmalar: 2 ta
Tushum: 268000 so'm
Yechim
js
function calculateRevenue(orders) {
  return orders.reduce((total, o) => total + o.amount, 0);
}

function printShortReport(orders) {
  console.log(`Tushum: ${calculateRevenue(orders)}`);
}

function printReport(orders) {
  console.log(`Buyurtmalar: ${orders.length} ta`);
  console.log(`Tushum: ${calculateRevenue(orders)} so'm`);
}

const today = [{ amount: 70000 }, { amount: 198000 }];
printShortReport(today);
printReport(today);

Konsolda:

text
Tushum: 268000
Buyurtmalar: 2 ta
Tushum: 268000 so'm

true/false o'rniga endi chaqiruvning o'zi gapiradi: printShortReport(today) — "qisqa hisobotni chiqar". Hisob bitta joyda (calculateRevenue) — u toza, uni test qilish oson. Ikki chiqaruvchi funksiya — fe'l (print) bilan, chunki ular ekranga yozadi. Bir qatorli arrow'dagi o — joiz: to'plam nomi orders. Qisqa hisobotda "so'm" yo'qligi ataylab saqlandi: refaktor xulqni o'zgartirmaydi, hatto u g'alati bo'lsa ham. Uni tuzatish — alohida, ongli qaror.

4-mashq: Vazifalar qadami — funksiyalarni bo'lish va izohlarni tozalash

Branch: refactor/funksiyalar-izohlar. Ilovaning xulqi o'zgarmaydi.

Funksiyalar. Eng uzunlari bo'linadi, har yangi funksiya — bitta ish:

Oldin Keyin
asosiy.js ro'yxat click tinglovchisi — 42 qator, switch ichida ikki optimistik amal katakniAlmashtir(id, zaxira), qatorniOchir(id, zaxira); har case — bitta chaqiruv
forma submit tinglovchisi — 18 qator yangiVazifaniQosh(); tinglovchida preventDefault() va chaqiruv
importTugma tinglovchisi — 19 qator importQil(); tinglovchi — () => importQil()
Oldin Keyin
sinxronla() — 40 qator: GET, versiya 0 → PUT, tekshirish, solishtirish, holat serverRoyxatiniOl(sozlama) va qisqargan sinxronla()
api.js sorovYubor() — 40 qator: sozlama, tarmoq, JSON, status sorovniTayyorla(sozlama), javobniOqi(javob, matn); sorovYubor — faqat tarmoq
render.js render() — 17 qator, 5 ish boshXabarniYoz(), filtrTugmalariniYoz(), hisobniYoz(); render() — qadamlar ro'yxati

sinxronla() dan ajralgan yangi funksiya:

js
// Server kalitni bilmasa (versiya 0) — o'zimizdagi ro'yxat ketadi.
// Server javobi ham import kabi tekshiriladi
async function serverRoyxatiniOl(sozlama) {
  let { vazifalar, versiya } = await api.royxat(sozlama);
  if (versiya === 0) {
    const joriy = holat.vazifalar.toJSON();
    ({ vazifalar } = await api.almashtir(joriy, sozlama));
  }
  return paketniOqi(eksportMatni(vazifalar));
}

sinxronla() ichida endi bitta qator va bitta "nega" izohi:

js
const toza = await serverRoyxatiniOl({ signal: pult.signal });
// Farq bo'lmasa — saqlash, tarqatish va chizish shart emas
if (eksportMatni(toza) !== eksportMatni(holat.vazifalar)) {
  royxatniYangila(toza);
}

render() — mundarija:

js
export function render() {
  royxat.replaceChildren(...korinadiganlar().map(qatorYasa));
  // Ro'yxatdan keyin: bosh xabar qatorlar soniga qaraydi
  boshXabarniYoz();
  filtrTugmalariniYoz();
  hisobniYoz();
  teglarniYoz();
  aloqaniYoz();
}

Diagrammada render() va uning qadamlari. Tartibga qarang: bosh xabar faqat ro'yxat chizilgandan keyin yoziladi — izoh shuni aytadi:

flowchart LR
  R["render()"] --> A["1. ro'yxat qatorlari"]
  R --> B["2. boshXabarniYoz"]
  R --> C["3. filtrTugmalariniYoz"]
  R --> D["4. hisobniYoz"]
  R --> E["5. teglarniYoz"]
  R --> F["6. aloqaniYoz"]
  A -. "qatorlar soni" .-> B

switch ning har case i endi bitta chaqiruv:

js
switch (amal) {
  case "almashtir":
    katakniAlmashtir(id, zaxira);
    break;
  case "ochir":
    qatorniOchir(id, zaxira);
    break;

Chaqiruvlar tartibi o'zgarmadi. AbortSignal.timeout hali ham fetch dan oldin yaratiladi, toJSON() GET javobidan keyin olinadi. katakniAlmashtir da qiymat ekrandagi o'zgarishdan oldin olinadi — "Ko'p uchraydigan xatolar" bo'limidagi tuzoq aynan shu.

Izohlar. O'chirildi ("nima"ni takrorlaydi): // Quyidagi beshtasi eksport qilinmaydi… (royxat.js — export yo'qligi o'zi aytadi), // querySelector — royxat'ning metodi… (render.js, o'quv izohi), // Internet uzildi yoki qaytdi va // Boshqa tablardagi o'zgarishlar (asosiy.js).

Qo'shildi ("nega"):

  • matnniYoz — bir xil matnni qayta yozish jonli hududni qayta o'qitishi mumkin;
  • eksportMatni — massiv ko'rinishidagi ikkinchi argument yangi maydonni eksportga sizdirmaydi;
  • royxatniUla — tahrirdagi vazifani boshqa tab yoki server o'chirgan bo'lishi mumkin;
  • boshqaTabdanYangila — nega saqlamaymiz va tarqatmaymiz (aks-sado);
  • serverdaBajar — oldingi nusxa nega kerak;
  • katakniAlmashtir, qatorniOchir — qiymat nega oldindan olinadi;
  • sorovYubor — javob tanasi nega ham try ichida;
  • render() — bosh xabar nega ro'yxatdan keyin.

Mana ulardan biri:

js
// Boshqa tab o'zgartirdi: ro'yxat xabarda keldi. Saqlamaymiz va
// tarqatmaymiz — yuborgan tab buni qilgan; qayta tarqatsak, tablar
// bir-biriga cheksiz javob qaytaradi (aks-sado)
function boshqaTabdanYangila(xabar) {

Tekshiruv. O'tgan darsdagidek: tekshiruv/ skriptlarining chiqishi o'zgarishdan oldin va keyin faylga yoziladi va git diff --no-index bilan solishtiriladi — farq yo'q, hamma qator . Keyin brauzerda tekshiruv/ssenariy.md: qo'shish, belgilash, tahrir, o'chirish, import, ikki tab, ?muvaffaqiyatsiz=1 bilan qaytarish. Hammasi avvalgidek.

Yechim (buyruqlar)
bash
git switch -c refactor/funksiyalar-izohlar
# ... funksiyalarni bo'ling, izohlarni tozalang, tekshiring ...
git add assets/js
git commit -m "refactor: uzun funksiyalar bo'lindi, \
\"nima\" izohlari olib tashlandi, \"nega\" izohlari qo'shildi"
git push -u origin refactor/funksiyalar-izohlar
gh pr create --fill
gh pr merge --merge

Natija: 5 fayl (api.js, asosiy.js, paket.js, render.js, royxat.js), +109 −65. Qatorlar ko'paydi — bu normal: yangi funksiya nomlari va "nega" izohlari joy egallaydi. Maqsad qisqa fayl emas, tez topiladigan va xavfsiz o'zgartiriladigan kod.

E'tibor bering: tekshiruvimiz hozircha qo'lda — skript chiqishini ko'z bilan va diff bilan solishtiramiz. Bu sekin va xato qilish oson. Keyingi darsda uni haqiqiy avtomatik testlarga aylantiramiz.

10. Real ishda

  • Kod ko'rib chiqishda eng ko'p yoziladigan izohlar: "bu funksiyani bo'lsa bo'ladimi?", "bu raqam nima?", "izoh koddan farq qilyapti". Kichik funksiyalardan iborat PR tezroq tekshiriladi.
  • ESLint qoidalari bir qismini avtomatik ushlaydi: max-lines-per-function, max-params, max-depth (ichma-ichlik), no-magic-numbers. Ular tavsiya to'plamida yo'q — chegarani jamoa o'zi tanlaydi (ESLint va Prettier darsida sinab ko'ramiz).
  • Intervyu. "Toza funksiya qanday bo'ladi?", "Qachon izoh yozasiz?", "Guard clause nima?" — ko'pincha kod parchasini ko'rsatib, "buni qanday yaxshilaysiz?" deb so'rashadi.

Xulosa

  • Funksiya bitta ish qiladi: ishini "va"siz bir gapda ayta olasiz; ichida bir daraja abstraksiya.
  • Ichma-ich shart o'rniga himoya shartlari (erta qaytish): avval xatolar, oxirida asosiy yo'l.
  • Parametr 0–2 ta, ko'p bo'lsa — options obyekti; flag argument o'rniga ikki nomli funksiya; so'rov va buyruq alohida.
  • Sehrli qiymat o'rniga nomli o'zgarmas (KUTISH_MS, MAKS_UZUNLIK).
  • Izoh "nega"ni aytadi; "nima" izohi va izohlangan kod o'chiriladi; eskirgan izoh — yolg'on.
  • Formatlash bahsini vosita (Prettier) hal qiladi; fayl gazeta kabi — yuqorida umumiy, pastda tafsilot.

Keyingi dars: Birinchi avtomatik test: node:test va assert — qo'lda solishtirish o'rniga kodni o'zi tekshiradigan testlar yozamiz va vazifalar ning tekshiruv skriptlarini testlarga aylantiramiz.

Manbalar

  • Robert C. Martin, "Clean Code", 2008 — 3-bob "Functions", 4-bob "Comments"
  • Martin Fowler, "Refactoring", 2-nashr, 2018 — "Extract Function", "Replace Nested Conditional with Guard Clauses", "Remove Flag Argument", "Replace Magic Literal"
  • Bertrand Meyer, "Object-Oriented Software Construction" — command-query separation tamoyili
  • ESLint qoidalari: max-lines-per-function, max-params, max-depth, no-magic-numbers — eslint.org/docs/latest/rules
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Toza kod: funksiyalar, izohlar va tuzilma — bitta ish, erta qaytish va "nega" izohi — IlmHamroh