Mundarija (35)
- Bu darsda
- 1. Nega bu kerak?
- 2. Kompozitsiya nima
- 2.1 Ikki funksiyani ulash
- 2.2 Bosqichlar bir-biriga mos bo'lsin
- 3. pipe: chapdan o'ngga
- 3.1 Istalgancha funksiyani ulash
- 3.2 Qadamma-qadam
- 3.3 Terminaldagi quvur
- 4. compose: o'ngdan chapga
- 4.1 Matematik tartib
- 4.2 Qaysi birini tanlash
- 5. Point-free uslub
- 5.1 Argumentni nomlamasdan
- 5.2 Tuzoq: map(parseInt)
- 5.3 Tuzoq: yo'qolgan this
- 6. Pipeline amalda
- 6.1 Massiv bosqichlari
- 6.2 Kuzatuv bosqichi
- 6.3 Asinxron pipe
- 7. Kelajak: pipeline operatori |>
- 8. Ko'p uchraydigan xatolar
- 8.1 Funksiyani chaqirib berish
- 8.2 pipe va compose tartibini adashtirish
- 8.3 Ikki argumentli funksiyani quvurga qo'yish
- 8.4 Bosqich return ni unutishi
- 8.5 Juda uzun quvur
- 9. Mashqlar
- 1-mashq (oson): Narx yorlig'i
- 2-mashq (o'rta): compose ni pipe dan
- 3-mashq (qiyin): Telefon raqamini me'yorlash
- 4-mashq: Vazifalar qadami — vazifa matnini tozalash quvuri
- 10. Real ishda
- Xulosa
- Manbalar
JavaScript'da kompozitsiya: compose va pipe bilan funksiyalarni ulash
Qisqacha: Kompozitsiya — kichik funksiyalarni ulab, bitta katta funksiya yasash: birining natijasi keyingisiga kirish bo'ladi.
pipe(f, g, h)funksiyalarni chapdan o'ngga bajaradi (h(g(f(x)))),compose(h, g, f)esa xuddi shuni o'ngdan chapga yozadi. Ikkalasi hamreducebilan bir necha qatorda yoziladi.
Bu darsda
- Funksiya kompozitsiyasi nima ekanini va u ichma-ich chaqiruvdan nimasi bilan yaxshi ekanini tushuntira olasiz.
pipevacomposeni o'zingizreducevareduceRightbilan yozasiz.- Point-free uslubda yozasiz va uning ikki mashhur tuzog'ini (
map(parseInt), yo'qolganthis) taniysiz. - Pipeline o'rtasiga kuzatuv bosqichini qo'yib, xatoni topa olasiz.
- Promise qaytaradigan bosqichlar uchun asinxron
pipeyasaysiz.
Oldin bilishingiz kerak: FP g'oyalari va deklarativ uslub, Immutability chuqur, Metod zanjiri va ma'lumot pipeline, reduce, Closure.
1. Nega bu kerak?
Immutability chuqur darsida ma'lumotni o'zgartirmasdan, har safar yangisini qaytarishni o'rgandik. Endi ana shunday toza funksiyalarni bir-biriga ulashni o'rganamiz.
«Bahor»dagi stajyor Sardorga yana ish berildi. Bron formasida mehmonlar ismini har xil yozadi: " dilshod AKA ", "MALIKA", " jasur". Administrator ro'yxatda hammasi bir xil ko'rinishini xohlaydi: "Dilshod aka". Sardor to'rtta kichik funksiya yozdi:
const chetlariniKes = (matn) => matn.trim();
const kichikHarf = (matn) => matn.toLowerCase();
const boshJoyniQisqart = (matn) => matn.replace(/\s+/g, " ");
const boshHarfKatta = (matn) => matn[0].toUpperCase() + matn.slice(1);
const ism = " dilshod AKA ";
const toza = boshHarfKatta(
boshJoyniQisqart(kichikHarf(chetlariniKes(ism))),
);
console.log(toza); // Dilshod akaIshlaydi. Lekin toza qatorini o'qing. Birinchi bajariladigan funksiya — chetlariniKes — qatorning o'rtasida yashiringan. Oxirgisi esa — boshHarfKatta — eng boshida turibdi. Ko'z chapdan o'ngga o'qiydi, kod esa ichkaridan tashqariga bajariladi. Yana bitta bosqich qo'shilsa, qavslarni sanashga to'g'ri keladi.
Metod zanjiri (Metod zanjiri) bu yerda yordam bermaydi. Satrda .boshHarfKatta() degan metod yo'q — u Sardorning o'z funksiyasi. Uni String.prototype ga qo'shish esa yomon odat: O'rnatilgan prototiplar darsida monkey patching nega xavfli ekanini ko'rgan edik.
Sardorga boshqa yo'l kerak: funksiyalarni tartib bilan ro'yxat qilib, ulardan bitta tayyor funksiya yasash. Shunda kod bunday o'qiladi: "chetlarini kes, kichik harf qil, bo'sh joyni qisqart, bosh harfni katta qil". Bu — kompozitsiya, va bugungi dars shu haqida.
2. Kompozitsiya nima
2.1 Ikki funksiyani ulash
Funksiya kompozitsiyasi (function composition) — ikki yoki undan ko'p funksiyani ulab, yangi funksiya yasash. Bunda bir funksiyaning natijasi keyingisiga kirish bo'ladi. Qisqasi — kompozitsiya.
Hayotdan o'xshatish: suv quvurlari. Santexnik bir quvurni filtrga, filtrni isitgichga, isitgichni jo'mrakka ulaydi. Suv bir uchidan kirib, hamma bo'g'indan o'tadi va boshqa uchidan chiqadi. Siz faqat jo'mrakni ochasiz — ichidagi bo'g'inlarni o'ylamaysiz.
Ikki funksiyani qo'lda ulash juda qisqa:
const chetlariniKes = (matn) => matn.trim();
const kichikHarf = (matn) => matn.toLowerCase();
const tozala = (matn) => kichikHarf(chetlariniKes(matn));
console.log(tozala(" MALIKA ")); // malikatozala — yangi funksiya. U ichida ikkita funksiyani ketma-ket chaqiradi. Tashqaridan esa oddiy funksiya: bitta argument oladi, bitta natija qaytaradi.
Endi bu "ulash" ishini ham funksiyaga aylantiramiz. Ikki funksiyani olib, ularning kompozitsiyasini qaytaradigan funksiya:
const ula = (birinchi, ikkinchi) => (x) => ikkinchi(birinchi(x));
const chetlariniKes = (matn) => matn.trim();
const kichikHarf = (matn) => matn.toLowerCase();
const tozala = ula(chetlariniKes, kichikHarf);
console.log(tozala(" MALIKA ")); // malikaula — yuqori tartibli funksiya: u funksiyalarni oladi va funksiya qaytaradi. Qaytgan arrow birinchi va ikkinchi ni closure orqali eslab qoladi.
2.2 Bosqichlar bir-biriga mos bo'lsin
Kompozitsiyaning bitta sharti bor: har bosqichning chiqishi keyingisining kirishiga mos kelsin. chetlariniKes satr qaytaradi, kichikHarf satr kutadi — mos. Agar o'rtadagi funksiya son qaytarsa-yu, keyingisi satr kutsa, quvur "oqib ketadi".
Ikkinchi shart — har bosqich bitta argument oladi. Quvurdan bitta oqim keladi, ikkita emas. Bunday funksiya bir argumentli (unary) deyiladi. Ikki argumentli funksiyani quvurga qanday qo'yish — keyingi darsning mavzusi.
Uchinchisi — shart emas, lekin juda muhim: bosqichlar toza bo'lsin (Pure funksiya va side effect). Toza bosqichni quvurning istalgan joyiga ko'chirish, olib tashlash va alohida sinash xavfsiz.
Tekshirib ko'ring:
ula(kichikHarf, chetlariniKes)vaula(chetlariniKes, kichikHarf)bir xil natija beradimi?
Javob
Bu ikki funksiya uchun — ha: tartib qanday bo'lmasin, natija "malika". Lekin bu tasodif. boshHarfKatta va kichikHarf ni almashtirsangiz, natija har xil bo'ladi: avval katta harf qilib, keyin hammasini kichik qilsangiz, katta harf yo'qoladi. Kompozitsiyada tartib odatda muhim.
3. pipe: chapdan o'ngga
3.1 Istalgancha funksiyani ulash
ula faqat ikkita funksiyani ulaydi. Sardorga to'rttasi kerak. Ertaga beshtasi kerak bo'ladi. Bizga istalgancha funksiyani oladigan versiya kerak.
E'tibor bering: bu aynan reduce ning ishi. Bizda funksiyalar massivi bor va boshlang'ich qiymat bor. Har qadamda akkumulyatorni navbatdagi funksiyadan o'tkazamiz:
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const chetlariniKes = (matn) => matn.trim();
const kichikHarf = (matn) => matn.toLowerCase();
const boshJoyniQisqart = (matn) => matn.replace(/\s+/g, " ");
const boshHarfKatta = (matn) => matn[0].toUpperCase() + matn.slice(1);
const ismniTozala = pipe(
chetlariniKes,
kichikHarf,
boshJoyniQisqart,
boshHarfKatta,
);
console.log(ismniTozala(" dilshod AKA ")); // Dilshod aka
console.log(ismniTozala("MALIKA")); // MalikaPipe — inglizcha "quvur". pipe ga funksiyalar bajarilish tartibida beriladi. ...bosqichlar — rest parametr: hamma argumentlar bitta massivga yig'iladi. kirish — quvurga kiradigan qiymat.
reduce qatorini so'z bilan o'qing: "natija — avval kirish; har bosqichda natijani bosqichga ber va qaytganini yangi natija qil". Ya'ni pipe(a, b, c)(x) — bu c(b(a(x))) ning boshqacha yozuvi.
Endi ismniTozala ni yuqoridan pastga o'qing. Bu Sardorning to'rtta qadami — aynan o'sha tartibda. Qavs sanash yo'q, ichkaridan tashqariga o'qish yo'q.
O'zingiz to'ldiring: pipe(kichikHarf, chetlariniKes) da birinchi bo'lib ishlaydigan funksiya — .
3.2 Qadamma-qadam
pipe ikki qadamda ishlaydi. Avval pipe(...) chaqiriladi va u hech narsani bajarmaydi — faqat funksiyalar ro'yxatini eslab qolgan yangi funksiya qaytaradi. Keyin o'sha yangi funksiya matn bilan chaqiriladi va reduce ishga tushadi. Quyidagi qadamlarda akkumulyator natija qanday o'zgarishini kuzating:
Ikki bosqichli tuzilish tasodifiy emas. Quvurni bir marta yig'asiz, keyin uni xohlagancha ishlatasiz. Masalan, map ga berasiz:
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const ismniTozala = pipe(
(matn) => matn.trim(),
(matn) => matn.toLowerCase(),
(matn) => matn[0].toUpperCase() + matn.slice(1),
);
const bronlar = [" MALIKA", "jasur ", "DILSHOD AKA"];
console.log(bronlar.map(ismniTozala));
// [ 'Malika', 'Jasur', 'Dilshod aka' ]Bosqichlarga nom berish shart emas: inline arrow ham bosqich. Lekin nomli bosqichlar quvurni gap kabi o'qiladigan qiladi va ularni boshqa quvurlarda qayta ishlatish mumkin.
3.3 Terminaldagi quvur
Bu g'oyani siz 01-qismda ko'rgansiz. Oqimlar, pipe va yo'naltirish darsida buyruqlarni | belgisi bilan ulagan edik:
cat menyu.txt | grep "osh" | sortHar buyruq oldingisining chiqishini oladi va o'z ishini qiladi. JavaScript'dagi pipe — aynan shu, faqat buyruqlar o'rnida funksiyalar. Nomi ham shundan olingan.
Tekshirib ko'ring:
pipe()— funksiyasiz — chaqirilsa,pipe()(5)nima qaytaradi?
Javob
5. Massiv bo'sh, shuning uchun reduce birorta ham callback chaqirmaydi va boshlang'ich qiymatni qaytaradi. Bo'sh quvur suvni o'zgartirmasdan o'tkazadi. Agar reduce ga boshlang'ich qiymat bermaganimizda, bo'sh massivda TypeError chiqardi (reduce).
4. compose: o'ngdan chapga
4.1 Matematik tartib
pipe ning egizagi bor — compose. U ham funksiyalarni ulaydi, lekin ular teskari tartibda yoziladi: oxirgi bajariladigan funksiya birinchi turadi.
Nega bunday g'alati tartib? Chunki matematikada kompozitsiya shunday yoziladi: f(g(x)) — avval g, keyin f. compose(f, g) ham xuddi f(g(x)) dagi tartibda o'qiladi. Ichma-ich chaqiruvdagi qavslarni olib tashlasangiz — compose qoladi.
Yozilishi pipe bilan bir xil. Faqat reduce o'rnida reduceRight — u massivni oxiridan boshiga aylanadi:
const compose = (...bosqichlar) => (kirish) =>
bosqichlar.reduceRight(
(natija, bosqich) => bosqich(natija),
kirish,
);
const chetlariniKes = (matn) => matn.trim();
const kichikHarf = (matn) => matn.toLowerCase();
const boshHarfKatta = (matn) => matn[0].toUpperCase() + matn.slice(1);
const ismniTozala = compose(boshHarfKatta, kichikHarf, chetlariniKes);
console.log(ismniTozala(" MALIKA ")); // Malikacompose(boshHarfKatta, kichikHarf, chetlariniKes) — bu boshHarfKatta(kichikHarf(chetlariniKes(x))) ning qavssiz yozuvi. Tartibni solishtiring: so'zlar aynan bir xil o'rinda turibdi.
flowchart LR
X["' MALIKA '"] --> A["chetlariniKes"]
A --> B["kichikHarf"]
B --> C["boshHarfKatta"]
C --> Y["'Malika'"]
P["pipe(chetlariniKes, kichikHarf, boshHarfKatta)"] -.-> A
K["compose(boshHarfKatta, kichikHarf, chetlariniKes)"] -.-> CDiagrammaga qarang: ma'lumot ikkala holatda ham bir xil yo'ldan o'tadi. Farq faqat yozuvda: pipe ro'yxati quvurning kirish uchidan, compose ro'yxati esa chiqish uchidan boshlanadi.
4.2 Qaysi birini tanlash
Natija bir xil, demak tanlov — o'qish qulayligi masalasi:
pipe |
compose |
|
|---|---|---|
| Tartib | bajarilish tartibida | teskari (matematik) |
| O'qilishi | yuqoridan pastga, "keyin" | "…ning natijasidan" |
| Qayerda | Ramda, lodash flow, RxJS |
Redux, lodash flowRight |
Jadvaldagi Ramda, lodash va RxJS — tayyor yordamchi funksiyalar to'plami bo'lgan kutubxonalar. Ularni hozir bilish shart emas: nomini tanib olsangiz yetarli.
Boshlovchiga pipe tabiiyroq: kod qanday yozilgan bo'lsa, shunday bajariladi. Shu kursda ham asosan pipe ishlatamiz. compose ni esa tanib olish kerak: Redux kabi kutubxonalar hujjatida u ko'p uchraydi.
Eng muhim qoida — loyiha ichida bittasini tanlab, hamma joyda shuni ishlatish. Bir faylda pipe, boshqasida compose bo'lsa, o'quvchi har safar "qaysi tartib edi?" deb o'ylaydi.
Tekshirib ko'ring:
compose(f, g, h)ga tengpipeqanday yoziladi?
Javob
pipe(h, g, f). compose da birinchi bajariladigan funksiya — oxirgisi (h), pipe da esa u birinchi yoziladi. Ro'yxat shunchaki teskari o'giriladi.
5. Point-free uslub
5.1 Argumentni nomlamasdan
bronlar.map(ismniTozala) ga qarang. Biz map((ism) => ismniTozala(ism)) deb yozmadik — funksiyaning o'zini berdik. Argument nomi ism umuman ko'rinmaydi.
Bu point-free uslub deyiladi: funksiyani argumentlarini tilga olmasdan, boshqa funksiyalardan yig'ib yozish. "Point" — matematikada argument ("nuqta") degani, "free" — "siz". Ya'ni "argumentsiz uslub".
pipe bilan yig'ilgan funksiya ham point-free: ismniTozala ta'rifida matn degan so'z yo'q, faqat bosqichlar ro'yxati bor. Qisqa va "nima qilinadi" ni aytadi, "qanday" ni emas — FP g'oyalari darsidagi deklarativ uslubning o'zi.
Point-free yozuv qulay, lekin unda ikkita mashhur tuzoq bor. Ikkalasi ham bir xil sababdan: funksiyani "yalang'och" berganda, uni kim va qanday chaqirishini siz nazorat qilmaysiz.
5.2 Tuzoq: map(parseInt)
Bu JavaScript intervyularidagi eng mashhur savollardan biri:
const narxlar = ["35000", "28000", "30000"];
console.log(narxlar.map(Number)); // [ 35000, 28000, 30000 ]
console.log(narxlar.map(parseInt)); // [ 35000, NaN, NaN ]Nega parseInt buzildi? Callback imzosi darsini eslang: map callback'ni uchta argument bilan chaqiradi — element, indeks va massiv. parseInt esa ikkinchi argumentni sanoq tizimi deb qabul qiladi (Matn ↔ son). Ya'ni aslida shunday chaqiruvlar bo'ldi:
console.log(parseInt("35000", 0)); // 35000
console.log(parseInt("28000", 1)); // NaN
console.log(parseInt("30000", 2)); // NaN0 — "o'zing aniqla" degani, shuning uchun birinchisi to'g'ri chiqdi. 1 lik sanoq tizimi umuman yo'q. Ikkilik tizimda esa 3 raqami yo'q, shuning uchun NaN. Number faqat bitta argumentga qaraydi — u bilan muammo yo'q.
Tuzatish: ortiqcha argumentlarni arrow bilan "to'sing" — narxlar.map((narx) => parseInt(narx, 10)). Qoida: point-free faqat funksiya qancha argument olishini aniq bilsangiz.
5.3 Tuzoq: yo'qolgan this
Ikkinchi tuzoq — metodni yalang'och berish. Uni this yo'qolishi va uni tiklash darsida ko'rgan edik:
const kassa = {
foiz: 10,
xizmatQosh(narx) {
return narx + (narx * this.foiz) / 100;
},
};
console.log([35000, 28000].map(kassa.xizmatQosh));TypeError: Cannot read properties of undefined (reading 'foiz')Tarjimasi: "undefined ning xususiyatlarini o'qib bo'lmaydi (foiz ni o'qishda)." kassa.xizmatQosh ni map ga berganda, metod obyektdan ajraldi. map uni oddiy funksiya sifatida chaqirdi. Qat'iy rejimda (modullar doim qat'iy) bunday chaqiruvda this — undefined.
Qat'iy rejimsiz eski skriptda bundan ham yomoni bo'ladi. U yerda this global obyektga aylanadi, this.foiz — undefined, natija esa jimgina [ NaN, NaN ]. Xato xabari yo'q — faqat noto'g'ri raqamlar (Strict mode).
Tuzatish: map((narx) => kassa.xizmatQosh(narx)) yoki map(kassa.xizmatQosh.bind(kassa)) (call, apply, bind). Qoida: metodni point-free bermang — uni arrow bilan o'rang.
Tekshirib ko'ring:
["1", "2", "3"].map(Number)va["1", "2", "3"].map(parseInt)natijalari qanday?
Javob
Birinchisi — [ 1, 2, 3 ]. Ikkinchisi — [ 1, NaN, NaN ]. parseInt("2", 1) — bir lik tizim yo'q; parseInt("3", 2) — ikkilik tizimda 3 raqami yo'q. map indeksni ikkinchi argument qilib bergani uchun.
6. Pipeline amalda
6.1 Massiv bosqichlari
Quvur bosqichlari satr bilan cheklanmaydi. Bosqich massiv olib, massiv qaytarishi ham mumkin. Unda Metod zanjiri darsidagi hisobotni qayta ishlatiladigan bo'laklarga ajratsa bo'ladi:
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const tolanganlar = (royxat) => royxat.filter((b) => b.tolandi);
const summalar = (royxat) => royxat.map((b) => b.summa);
const jami = (sonlar) => sonlar.reduce((s, son) => s + son, 0);
const tushum = pipe(tolanganlar, summalar, jami);
const qarz = pipe(
(royxat) => royxat.filter((b) => !b.tolandi),
summalar,
jami,
);
const buyurtmalar = [
{ stol: 3, summa: 120000, tolandi: true },
{ stol: 1, summa: 45000, tolandi: false },
{ stol: 7, summa: 30000, tolandi: true },
];
console.log(tushum(buyurtmalar)); // 150000
console.log(qarz(buyurtmalar)); // 45000summalar va jami ikki quvurda ham ishladi. Metod zanjirida buning uchun butun zanjirni qayta yozish kerak edi. Kompozitsiyada esa bosqich — mustaqil funksiya: uni nomlaysiz, sinaysiz, istalgan quvurga qo'yasiz. Har bosqich yangi massiv qaytaradi va kirishni o'zgartirmaydi — o'tgan darsdagi immutability qoidasi shu yerda ishlayapti.
6.2 Kuzatuv bosqichi
Quvur noto'g'ri natija bersa, qaysi bosqich aybdor? Metod zanjirida o'rtaga console.log qo'yish noqulay edi. Kompozitsiyada esa buning chiroyli yo'li bor — kuzatuv bosqichi. U qiymatni konsolga chiqaradi va o'zgartirmasdan qaytaradi:
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const kuzat = (yorliq) => (qiymat) => {
console.log(`${yorliq}:`, qiymat);
return qiymat;
};
const ismniTozala = pipe(
(matn) => matn.trim(),
kuzat("trim"),
(matn) => matn.toLowerCase(),
kuzat("kichik"),
(matn) => matn[0].toUpperCase() + matn.slice(1),
);
console.log(ismniTozala(" JASUR "));Konsolda:
trim: JASUR
kichik: jasur
Jasurkuzat("trim") — funksiya qaytaradigan funksiya. Yorliqni bir marta berasiz, qaytgan funksiya esa quvurga bosqich bo'lib kiradi. Ingliz tilidagi kutubxonalarda bunday bosqich tap deb ataladi — "tegib o'tish".
Bu bosqich toza emas: u konsolga yozadi. Lekin bu ataylab qilingan, vaqtinchalik yon ta'sir — xato topilgach, kuzat qatorlarini o'chirasiz. Qiymatni o'zgartirmagani uchun quvurning ishini buzmaydi.
6.3 Asinxron pipe
Ba'zi bosqichlar Promise qaytaradi: serverdan menyu olish, faylni o'qish. Oddiy pipe ularni kutmaydi — keyingi bosqichga Promise'ning o'zi tushib qoladi. Yechim — akkumulyatorni Promise qilish va har bosqichni then bilan ulash (Promise zanjiri):
const pipeAsync = (...bosqichlar) => (kirish) =>
bosqichlar.reduce(
(vada, bosqich) => vada.then(bosqich),
Promise.resolve(kirish),
);
const menyuniOl = async (turkum) => [
{ nom: "Osh", narx: 35000, turkum },
{ nom: "Manti", narx: 30000, turkum },
];
const arzonlar = (taomlar) => taomlar.filter((t) => t.narx < 32000);
const nomlari = (taomlar) => taomlar.map((t) => t.nom);
const arzonTaomlar = pipeAsync(menyuniOl, arzonlar, nomlari);
console.log(await arzonTaomlar("milliy")); // [ 'Manti' ]menyuniOl — async funksiya, u Promise qaytaradi. Qolgan ikki bosqich oddiy. then ikkalasini ham to'g'ri ulaydi: callback oddiy qiymat qaytarsa — keyingi then uni oladi, Promise qaytarsa — avval kutadi. Haqiqiy loyihada menyuniOl ichida fetch bo'ladi. Biz natijani tez va bir xil chiqishi uchun tayyor massiv qaytardik.
Natija ham Promise, shuning uchun uni await bilan oldik. Biror bosqich xato tashlasa, qolganlari o'tkazib yuboriladi va xato oxirgi await da chiqadi — oddiy Promise zanjiridagidek.
Tekshirib ko'ring:
kuzatbosqichi qiymatni qaytarishni unutsa (return qiymatbo'lmasa), quvurning keyingi bosqichi nima oladi?
Javob
undefined. Arrow { } jingalak qavsli tanada return siz undefined qaytaradi. Keyingi bosqich undefined.toLowerCase() ni chaqiradi va TypeError chiqadi. Kuzatuv bosqichining asosiy qoidasi — kirgan qiymatni o'zgartirmasdan qaytarish.
7. Kelajak: pipeline operatori |>
JavaScript'ga quvur uchun maxsus operator qo'shish taklif qilingan: |>. Taklif TC39 qo'mitasida (JavaScript standartini ishlab chiquvchi guruh) 2-bosqichda turibdi — 2026-yil oktabr holatiga. Ya'ni bu hali til emas: hech bir brauzer va Node uni bajarmaydi.
Taklif qilingan yozuv taxminan shunday ko'rinadi (% — oldingi bosqichning natijasi):
// Hali ishlamaydi — faqat taklif (TC39, 2-bosqich)
const toza = ism |> %.trim() |> %.toLowerCase() |> boshHarfKatta(%);Operator qabul qilinsa, pipe funksiyasiz ham ichma-ich chaqiruvdan qutulish mumkin bo'ladi. Ungacha — o'zimiz yozgan pipe yoki kutubxonadagisi. Sintaksis va % belgisi hali o'zgarishi mumkin, shuning uchun bu kodni loyihaga yozmang.
8. Ko'p uchraydigan xatolar
8.1 Funksiyani chaqirib berish
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const chetlariniKes = (matn) => matn.trim();
const tozala = pipe(chetlariniKes());TypeError: Cannot read properties of undefined (reading 'trim')Tarjimasi: "undefined ning xususiyatlarini o'qib bo'lmaydi (trim ni o'qishda)." chetlariniKes() qavs bilan yozildi — funksiya darhol argumentsiz chaqirildi va matn — undefined. pipe ga funksiya emas, uning natijasini bermoqchi bo'ldik. Tuzatish: quvurga funksiyaning nomi qavssiz beriladi: pipe(chetlariniKes). Qavs — "hozir chaqir", qavssiz — "mana, keyin chaqirasan".
8.2 pipe va compose tartibini adashtirish
compose(chetlariniKes, boshHarfKatta) yozib, "avval kes, keyin katta qil" deb o'ylash. Aslida compose oxiridan boshlaydi. Xato xabari chiqmasligi mumkin — natija shunchaki boshqacha bo'ladi. Tuzatish: loyihada faqat bittasini ishlating; pipe ni tanlasangiz, bu xato umuman bo'lmaydi.
8.3 Ikki argumentli funksiyani quvurga qo'yish
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const chegirma = (foiz, narx) => narx - (narx * foiz) / 100;
const narxniHisobla = pipe(Number, chegirma);
console.log(narxniHisobla("35000")); // NaNQuvur chegirma ga bitta qiymat beradi — u foiz ga tushadi, narx esa undefined. Natija NaN, xato yo'q. Tuzatish: bosqichni bir argumentli qiling — (narx) => chegirma(10, narx). Buni tizimli hal qilish yo'li — currying, u keyingi darsda.
8.4 Bosqich return ni unutishi
Jingalak qavsli arrow return siz undefined qaytaradi. Keyingi bosqich undefined oladi va yiqiladi — lekin xato keyingi bosqichda chiqadi, aybdor bosqichda emas. Tuzatish: xato chiqqan bosqichdan oldingisiga kuzat qo'ying.
8.5 Juda uzun quvur
O'n beshta bosqichli quvur o'qishga qiyin. Ichma-ich chaqiruvdan qochib, boshqa muammoga tushdik. Tuzatish: bosqichlarni guruhlab, kichik quvurlarga nom bering: const ismniTozala = pipe(...), const telefonniTozala = pipe(...), keyin ularni katta quvurda ulang. Quvur ham funksiya — uni boshqa quvurga bosqich qilib qo'yish mumkin.
9. Mashqlar
1-mashq (oson): Narx yorlig'i
pipe bilan yorliqYasa funksiyasini yig'ing. Bosqichlar: Math.round (yaxlitlash), sonni satrga aylantirish (String), oxiriga " so'm" qo'shish. pipe ning ta'rifini darsdan ko'chiring.
console.log(yorliqYasa(34999.6)); // 35000 so'mYechim
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const yorliqYasa = pipe(
Math.round,
String,
(matn) => `${matn} so'm`,
);
console.log(yorliqYasa(34999.6)); // 35000 so'mMath.round va String — tayyor bir argumentli funksiyalar, ularni point-free berdik. Uchinchi bosqich — o'zimizning arrow. Aslida shablon satr String siz ham sonni satrga aylantirardi; bosqich bu yerda tartibni ko'rsatish uchun.
2-mashq (o'rta): compose ni pipe dan
pipe tayyor deb hisoblang. compose ni reduceRight siz, faqat pipe va massiv metodi bilan yozing. Ishora: compose — funksiyalar ro'yxati teskari o'girilgan pipe. Massivni o'zgartirmasdan teskari qilish — toReversed.
const ikkiBaravar = (x) => x * 2;
const birQosh = (x) => x + 1;
console.log(compose(ikkiBaravar, birQosh)(5)); // 12Yechim
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const compose = (...bosqichlar) => pipe(...bosqichlar.toReversed());
const ikkiBaravar = (x) => x * 2;
const birQosh = (x) => x + 1;
console.log(compose(ikkiBaravar, birQosh)(5)); // 12
console.log(pipe(ikkiBaravar, birQosh)(5)); // 11compose(ikkiBaravar, birQosh) — avval birQosh (5 → 6), keyin ikkiBaravar (6 → 12). pipe da esa tartib teskari: 5 → 10 → 11. toReversed yangi massiv qaytaradi, bosqichlar ning o'zi o'zgarmaydi.
3-mashq (qiyin): Telefon raqamini me'yorlash
Bron formasiga telefon har xil kiritiladi: "90 000-00-00", "+998 (90) 000 00 00", "998900000000". telefonniMeyorla quvurini yig'ing. U hammasini +998900000000 ko'rinishiga keltirsin. Bosqichlar:
- raqam bo'lmagan hamma belgini o'chirish (
\D— Belgilar va belgi klasslari); - 9 ta raqam bo'lsa, boshiga
998qo'shish; - boshiga
+qo'shish.
Yechim
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const faqatRaqam = (matn) => matn.replace(/\D/g, "");
const kodQosh = (raqam) =>
raqam.length === 9 ? `998${raqam}` : raqam;
const plyusQosh = (raqam) => `+${raqam}`;
const telefonniMeyorla = pipe(faqatRaqam, kodQosh, plyusQosh);
const kiritilganlar = [
"90 000-00-00",
"+998 (90) 000 00 00",
"998900000000",
];
console.log(kiritilganlar.map(telefonniMeyorla));Konsolda:
[ '+998900000000', '+998900000000', '+998900000000' ]Har bosqich bitta ish qiladi va alohida sinaladi: faqatRaqam("+998 (90)") → "99890". Tartib muhim: plyusQosh ni birinchi qo'ysangiz, faqatRaqam plyusni yana o'chirib yuboradi. Haqiqiy loyihada 2-bosqichdan keyin uzunlikni ham tekshirish kerak — buni Xato va bo'sh qiymatlar bilan FP uslubida ishlash darsida qilamiz.
4-mashq: Vazifalar qadami — vazifa matnini tozalash quvuri
vazifalar kodini o'zgartirmaymiz: quvurni avval alohida faylda sinaymiz. kurs/mashqlar/12/24-pipe/matn-quvuri.mjs faylida matnniTozala quvurini yig'ing. U foydalanuvchi yozgan vazifa matnini tartibga keltirsin: chetlarini kessin, ichidagi ketma-ket bo'sh joylarni bittaga qisqartirsin, birinchi harfni katta qilsin. Uni uchta matnda sinang: " non olish", "#bozor sabzi", "".
Yechim
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const chetlariniKes = (matn) => matn.trim();
const boshJoyniQisqart = (matn) => matn.replace(/\s+/g, " ");
const boshHarfKatta = (matn) =>
matn.slice(0, 1).toUpperCase() + matn.slice(1);
const matnniTozala = pipe(
chetlariniKes,
boshJoyniQisqart,
boshHarfKatta,
);
for (const matn of [" non olish", "#bozor sabzi", ""]) {
console.log(`[${matnniTozala(matn)}]`);
}Konsolda:
[Non olish]
[#bozor sabzi]
[]Bo'sh matnga e'tibor bering. Darsdagi boshHarfKatta matn[0].toUpperCase() bilan yozilgan edi — bo'sh satrda matn[0] undefined va TypeError chiqardi. slice(0, 1) esa bo'sh satrga bo'sh satr qaytaradi. Quvur bosqichi chegaraviy holatlarni ham ko'tarsin. Teg bilan boshlangan matnda # "katta" bo'lmaydi — bu to'g'ri xulq.
git add 12/24-pipe/matn-quvuri.mjs
git commit -m "12/24: vazifa matnini tozalash quvuri (pipe)"10. Real ishda
- Kutubxonalar. Lodash'da
flow(pipe) vaflowRight(compose), Ramda'dapipevacomposebor. RxJS (oqimlar bilan ishlash kutubxonasi) da operatorlar.pipe(...)ichida ulanadi — Angular loyihalarida har kuni ko'rasiz. - Redux. Redux kutubxonasi o'z
composefunksiyasini eksport qiladi. U bilan middleware'lar — har amal o'tadigan oraliq funksiyalar — ulanadi. React'ni o'rganganingizda (17-qismdan) uchratasiz. - Middleware. Express (server freymvorki, 25-qismda) so'rovni bir necha funksiyadan ketma-ket o'tkazadi — bu ham quvur g'oyasi, faqat boshqacha shaklda.
- Ma'lumotni tozalash. Formadan kelgan qiymatni me'yorlash, CSV qatorini o'qish, matnni slug'ga aylantirish — kichik bosqichlar quvuri sifatida yoziladi va har bosqich alohida sinaladi.
- Intervyu. "
composeni yozing", "pipevacomposefarqi nima?", "['1','2','3'].map(parseInt)nima qaytaradi?" — frontend intervyularining klassik savollari.
Xulosa
- Kompozitsiya — funksiyalarni ulash: birining natijasi keyingisining kirishi bo'ladi; bosqichlar bir argumentli va toza bo'lsin.
pipe(f, g, h)— chapdan o'ngga, bajarilish tartibida;reducebilan yoziladi.compose(h, g, f)— xuddi shu, matematik (teskari) tartibda;reduceRightbilan.- Point-free — funksiyani argumentini tilga olmasdan berish;
map(parseInt)va metodni yalang'och berish — uning ikki tuzog'i. kuzatbosqichi qiymatni chiqarib, o'zgartirmasdan qaytaradi;pipeAsyncPromise qaytaradigan bosqichlarnithenbilan ulaydi.|>operatori — hali taklif (TC39, 2-bosqich), loyihada ishlatilmaydi.
Keyingi dars: Currying va partial application — ikki argumentli funksiyani quvurga sig'adigan bir argumentli funksiyalar zanjiriga aylantirishni o'rganamiz.
Manbalar
- MDN: "Array.prototype.reduce", "Array.prototype.reduceRight", "parseInt" — developer.mozilla.org
- TC39: "Pipe Operator (|>) for JavaScript", Stage 2 — github.com/tc39/proposal-pipeline-operator
- Redux hujjatlari:
compose— redux.js.org/api/compose - Lodash hujjatlari:
_.flow,_.flowRight— lodash.com/docs
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!