Mundarija (39)
- Bu darsda
- 1. Nega bu kerak?
- 2. Iteratorni qo'lda aylantiramiz
- 2.1 Symbol.iterator — xatcho'p beradigan metod
- 2.2 { value, done } — iterator natijasi
- 2.3 Iterable va iterator — ikki xil rol
- 3. for...of ichida nima bo'ladi
- 3.1 for...of ni qo'lda yozamiz
- 3.2 Qadamma-qadam
- 3.3 break — erta to'xtash
- 4. Spread, destructuring va boshqalar ham shu protokolda
- 4.1 Kuzatuvchi yordamchi
- 4.2 Destructuring — faqat kerakligicha
- 4.3 Spread va rest — oxirigacha
- 4.4 Iterable qabul qiladigan joylar
- 5. Qaysi qiymatlar iterable?
- 5.1 Ro'yxat
- 5.2 Satr — harflar bo'yicha, kod birliklari bo'yicha emas
- 5.3 Tekshiruvchi funksiya
- 5.4 Oddiy obyektni qanday aylanamiz?
- 6. Iterator — bir martalik va "jonli"
- 6.1 Iterator bir marta o'qiladi
- 6.2 Iterator nusxa olmaydi
- 6.3 Iterator oldindan hisoblamaydi
- 7. Ko'p uchraydigan xatolar
- 7.1 TypeError: menyu is not iterable
- 7.2 new Map(obyekt) — boshqa so'zlar, bir xil sabab
- 7.3 Kvadrat va jingalak qavsni chalkashtirish
- 7.4 Iteratorni ikki marta ishlatish
- 7.5 Array.from jim qoladi
- 7.6 for...in ni for...of o'rniga yozish
- 8. Mashqlar
- 1-mashq (oson): Xatcho'pni qo'lda suring
- 2-mashq (o'rta): Birinchi N tasi
- 3-mashq (qiyin): O'z for...of ingiz
- 4-mashq: Vazifalar qadami — royxat iterable mi? (mashqlar repo)
- 9. Real ishda
- Xulosa
- Manbalar
JavaScript iteratsiya protokoli: for...of ichida nima bo'ladi
Qisqacha:
for...of, spread ([...x]) va massiv destructuring bitta kelishuvga tayanadi — iteratsiya protokoliga. Qiymatlar to'plami (iterable)[Symbol.iterator]()metodi bilan iterator beradi. Iterator esa harnext()chaqiruvida{ value, done }obyektini qaytaradi.for...ofshunext()nidone: truekelguncha qayta-qayta chaqiradi.
Bu darsda
- Iterable va iterator farqini tushuntira olasiz.
- Iteratorni qo'lda
next()bilan aylanib,{ value, done }natijasini o'qiy olasiz. for...of, spread va destructuring ichida nechtanext()chaqirilishini ko'rasiz.- Qaysi o'rnatilgan turlar iterable ekanini bilasiz va buni kod bilan tekshira olasiz.
x is not iterablexatosini o'qib, tuzata olasiz.
Oldin bilishingiz kerak: Massiv bo'ylab yurish, Massiv destructuring, Spread va rest, Map, Well-known symbols.
1. Nega bu kerak?
for...of ni 08-qismdan beri ishlatib kelyapsiz. U massivda ishlaydi, satrda ishlaydi, Map da ham ishlaydi. 09-qismda u querySelectorAll qaytargan NodeList ni ham aylangan edi. Lekin «Bahor» menyusini oddiy obyekt qilib yozsangiz:
const menyu = { osh: 35000, manti: 30000 };
for (const taom of menyu) {
console.log(taom);
}Konsolda:
TypeError: menyu is not iterableNega massiv "iterable", obyekt esa yo'q? Bu savol kursda bir necha marta chiqdi va har safar "keyinroq ochamiz" degan edik:
- Massiv bo'ylab yurish darsida
entries()natijasi konsoldaObject [Array Iterator] {}bo'lib chiqqan edi. - Massivga o'xshash obyektlar darsida "birma-bir berish qobiliyati" degan ibora bor edi.
Mapdarsida: "for...ofnega massivda ham,Mapda ham ishlaydi?"
Uchala savolning javobi bitta. Bugun shu javobni ochamiz.
Hayotdan o'xshatish: kitob va xatcho'p. Kitob — o'qisa bo'ladigan narsa, ichida betlar bor. Xatcho'p — qayerda to'xtaganingizni eslaydigan qog'oz tasma. Siz xatcho'pdan "keyingi bet"ni so'raysiz, u beradi va bir bet oldinga suriladi. Kitob tugasa, xatcho'p "tugadi" deydi. Bitta kitobga ikki kishi ikkita xatcho'p qo'yib, bir-biriga xalaqit bermay o'qiy oladi.
JavaScript'da kitob — iterable, xatcho'p — iterator. for...of esa o'quvchi: xatcho'p oladi va "keyingisi" deb so'rayveradi.
2. Iteratorni qo'lda aylantiramiz
2.1 Symbol.iterator — xatcho'p beradigan metod
Well-known symbols darsida Symbol.iterator bilan tanishgansiz: bu JavaScript'ning o'zi oldindan e'lon qilgan maxsus symbol. Massivda shu symbol kalitida metod turadi. Uni chaqirib ko'ramiz:
const narxlar = [35000, 28000, 30000];
const iterator = narxlar[Symbol.iterator]();
console.log(iterator);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());Konsolda:
Object [Array Iterator] {}
{ value: 35000, done: false }
{ value: 28000, done: false }
{ value: 30000, done: false }
{ value: undefined, done: true }Qatorma-qator:
narxlar[Symbol.iterator]— kaliti symbol bo'lgan xususiyat. Uni nuqta bilan yozib bo'lmaydi, faqat kvadrat qavs bilan (Computed kalitlar). Ichida — funksiya, oxiridagi()uni chaqiradi.- Natija —
Object [Array Iterator] {}. Bu iterator: massivning o'zi emas, uning ustidagi "xatcho'p". - Har
next()bitta qiymat beradi va xatcho'pni bir qadam suradi. - Qiymatlar tugagach —
{ value: undefined, done: true }.
Esingizdami, Massiv bo'ylab yurish darsida values() ham xuddi shunday Object [Array Iterator] {} qaytargan edi. Bu tasodif emas — ular bitta metod:
const narxlar = [35000, 28000];
console.log(narxlar[Symbol.iterator] === narxlar.values); // true2.2 { value, done } — iterator natijasi
next() har safar ikki xususiyatli obyekt qaytaradi. Uni iterator natijasi (iterator result) deyishadi:
| Xususiyat | Ma'nosi |
|---|---|
value |
navbatdagi qiymat |
done |
false — hali bor; true — tugadi |
Nega faqat value emas? Chunki undefined ham oddiy qiymat bo'lishi mumkin. [undefined, 5] massivida birinchi next() { value: undefined, done: false } beradi. Agar faqat value qaytsa, "massiv tugadi" va "navbatdagi qiymat undefined" ni farqlab bo'lmasdi. done bayrog'i shu ikkalasini ajratadi.
Maslahat:
done: truekelgandan keyin hamnext()ni chaqirsa bo'ladi — xato chiqmaydi, har safar yana{ value: undefined, done: true }keladi. Iterator boshiga qaytmaydi: o'qilgan kitobga qo'yilgan xatcho'p oxirgi betda qoladi.
Tekshirib ko'ring:
const it = "osh"[Symbol.iterator]();dan keyinit.next()ni to'rt marta chaqirsak, to'rtinchisi nima qaytaradi?
Javob
{ value: undefined, done: true }. Satr ham iterable: birinchi uch chaqiruv "o", "s", "h" harflarini beradi, to'rtinchisi "tugadi" deydi. Satr iteratori konsolda Object [String Iterator] {} bo'lib ko'rinadi.
2.3 Iterable va iterator — ikki xil rol
Endi ikki atamani aniq ajratamiz.
Iterable (inglizcha "aylanib chiqsa bo'ladigan") — [Symbol.iterator]() metodi bor obyekt. Bu metod har chaqirilganda iterator qaytaradi. Massiv, satr, Map — iterable. Bu kitob.
Iterator — next() metodi bor obyekt. next() har chaqirilganda { value, done } qaytaradi va o'zi qayerda turganini eslab qoladi. Bu xatcho'p.
Iteratsiya protokoli (iteration protocol) — shu ikki rol haqidagi kelishuv. "Protokol" — ikki tomon oldindan kelishib olgan qoida: kim qaysi metodni beradi, u nima qaytaradi. Protokol klass ham, funksiya ham emas. Qoidaga amal qilgan har qanday obyekt for...of bilan ishlaydi.
Diagrammada for...of qanday ishlashini kuzating:
flowchart TD
A["for (const x of taomlar)"] --> B["taomlar[Symbol.iterator]()"]
B --> C["iterator"]
C --> D["iterator.next()"]
D --> E{"done?"}
E -- "false" --> F["x = value<br/>sikl tanasi"]
F --> D
E -- "true" --> G["sikl tugadi"][Symbol.iterator]() faqat bir marta, boshida chaqiriladi. Keyin faqat next() aylanadi.
Yana bir nozik joy: massiv iteratorining o'zi ham iterable. Uning [Symbol.iterator]() metodi o'zini qaytaradi:
const taomlar = ["osh", "manti"];
const iterator = taomlar[Symbol.iterator]();
console.log(iterator[Symbol.iterator]() === iterator); // trueShuning uchun for (const x of taomlar.values()) ham ishlaydi: for...of iteratordan "xatcho'p" so'raydi, u esa o'zini beradi.
Tekshirib ko'ring: Massiv iterable mi, iterator mi?
narxlar.values()chi?
Javob
Massiv — iterable: unda [Symbol.iterator]() bor, lekin next() yo'q (narxlar.next — undefined). narxlar.values() — iterator: unda next() bor. U o'zini qaytaradigan [Symbol.iterator]() ga ham ega, shuning uchun bir vaqtda iterable ham.
3. for...of ichida nima bo'ladi
3.1 for...of ni qo'lda yozamiz
Diagrammadagi qadamlarni oddiy while sikli bilan yozib ko'ramiz:
const taomlar = ["osh", "lag'mon", "manti"];
const iterator = taomlar[Symbol.iterator]();
let natija = iterator.next();
while (!natija.done) {
const taom = natija.value;
console.log(taom);
natija = iterator.next();
}Konsolda:
osh
lag'mon
mantiBu kod for (const taom of taomlar) { console.log(taom); } bilan bir xil ishni qiladi. for...of — shu sakkiz qatorning qisqa yozuvi, xolos. JavaScript dvigateli next() chaqiruvlarini va done tekshiruvini siz uchun yashirib bajaradi.
3.2 Qadamma-qadam
Keyingi ni bosib, ikki taomli massivda har next() nima qaytarishini va sikl qachon to'xtashini kuzating:
Asosiy kuzatuv: n ta element uchun next() n + 1 marta chaqiriladi. Oxirgi chaqiruv qiymat bermaydi — faqat "tugadi" deydi.
3.3 break — erta to'xtash
for...of ichida break bo'lsa, qolgan next() lar chaqirilmaydi:
const narxlar = [35000, 28000, 30000, 5000];
for (const narx of narxlar) {
if (narx < 30000) {
console.log(`Birinchi arzoni: ${narx}`);
break;
}
}Konsolda:
Birinchi arzoni: 28000Iterator ikki marta so'raldi — 35000 va 28000. Qolgan ikki element umuman o'qilmadi. Bu oddiy massivda kichik tejash. Lekin keyingi darslarda qiymatlarni so'ralgandagina hisoblaydigan iteratorlarni ko'ramiz — u yerda bu farq juda katta bo'ladi.
Tekshirib ko'ring: Uch elementli massivni
for...ofbilan oxirigacha aylansak,next()necha marta chaqiriladi?breakbilan birinchi elementda chiqsak-chi?
Javob
Oxirigacha — 4 marta: uchta qiymat uchun va bitta "tugadi" (done: true) uchun. Birinchi elementda break — 1 marta: birinchi qiymat olindi, keyin sikl to'xtadi va boshqa so'ralmadi.
4. Spread, destructuring va boshqalar ham shu protokolda
4.1 Kuzatuvchi yordamchi
Ichkarida nechta next() chaqirilishini ko'rish uchun kichik yordamchi yozamiz. U istalgan iterable'ni oladi va har next() ni konsolga yozib turadi. Keyin uni destructuring va spread bilan sinaymiz:
function kuzat(iterable) {
const ichki = iterable[Symbol.iterator]();
return {
[Symbol.iterator]() {
return this;
},
next() {
const natija = ichki.next();
console.log("next() →", natija);
return natija;
},
};
}
const taomlar = ["osh", "lag'mon", "manti"];
const [birinchi, ikkinchi] = kuzat(taomlar);
console.log(birinchi, ikkinchi);
console.log("---");
const nusxa = [...kuzat(taomlar)];
console.log(nusxa.length);Konsolda:
next() → { value: 'osh', done: false }
next() → { value: "lag'mon", done: false }
osh lag'mon
---
next() → { value: 'osh', done: false }
next() → { value: "lag'mon", done: false }
next() → { value: 'manti', done: false }
next() → { value: undefined, done: true }
3kuzat — o'zimiz yasagan iterator: unda next() ham, o'zini qaytaradigan [Symbol.iterator]() ham bor. Ishni esa haqiqiy iteratorga (ichki) topshiradi, faqat o'rtada "josuslik" qiladi. Bunday obyektlarni qanday yasashni keyingi darsda batafsil o'rganamiz. Hozir uni tayyor asbob sifatida ishlating va chiqishning ikki yarmiga qarang.
4.2 Destructuring — faqat kerakligicha
"---" dan yuqorisi: ikki o'zgaruvchi — ikki next(). Uchinchi taom so'ralmadi ham. Massiv destructuring aslida "massivni" emas, istalgan iterable'ni ochadi: u indeks bo'yicha emas, next() bilan birma-bir oladi. Shuning uchun satrni ham ochsa bo'ladi: const [harf] = "Bahor"; — harf ga "B" tushadi.
4.3 Spread va rest — oxirigacha
"---" dan pastki qism: spread done: true kelguncha so'radi — uch qiymat va bitta "tugadi". Destructuring'dagi rest (const [bosh, ...qolgan] = ...) ham xuddi shunday: birinchisini oladi, qolganini oxirigacha yig'adi.
4.4 Iterable qabul qiladigan joylar
Bitta protokol — juda ko'p joyda ishlaydi:
| Yozuv | Nima qiladi |
|---|---|
for (const x of it) |
har qiymat uchun sikl |
[...it], f(...it) |
hammasini massivga yoki argumentlarga yoyadi |
const [a, b] = it |
kerakligicha next() |
Array.from(it) |
massiv yasaydi |
new Map(it), new Set(it) |
juftliklardan / qiymatlardan to'plam |
Object.fromEntries(it) |
[kalit, qiymat] juftliklaridan obyekt |
Masalan, Math.max(...new Set([3, 9, 2])) — 9: Set iterable, spread uni argumentlarga yoydi. Object.fromEntries(menyu) esa Map ni oddiy obyektga aylantiradi. Ularning hech biri "bu massivmi?" deb so'ramaydi — faqat "Symbol.iterator bormi?" deb so'raydi.
Diqqat: Obyekt ichidagi spread
{ ...x }boshqa mexanizm. U iteratsiya protokolini ishlatmaydi — obyektning o'z xususiyatlarini ko'chiradi (Spread va rest). Shuning uchun{ ...menyu }oddiy obyektda ishlaydi,[...menyu]esaTypeErrorberadi. Kvadrat qavs — protokol, jingalak qavs — xususiyatlar.
Tekshirib ko'ring:
const [a, b, c, d] = ["osh", "manti"];qatoridanext()necha marta chaqiriladi vadnima bo'ladi?
Javob
Uch marta. Birinchi ikkisi "osh" va "manti" ni berdi, uchinchisi done: true qaytardi. Iterator tugaganini bilgach, destructuring boshqa so'ramaydi: c va d — ikkalasi undefined. "Tugadi" degan iteratordan qayta so'rash ma'nosiz.
5. Qaysi qiymatlar iterable?
5.1 Ro'yxat
Kursda uchragan turlarni ikkiga ajratamiz:
| Iterable | Iterable emas |
|---|---|
| massiv, satr | oddiy obyekt {} |
Map, Set |
son, mantiqiy qiymat |
arguments, NodeList |
null, undefined |
| typed array'lar | WeakMap, WeakSet |
keys(), values(), entries() natijalari |
Map aylanganda [kalit, qiymat] juftliklarini beradi. Shuning uchun for...of ichida destructuring qulay:
const menyu = new Map([
["osh", 35000],
["manti", 30000],
]);
for (const [taom, narx] of menyu) {
console.log(`${taom}: ${narx} so'm`);
}Konsolda:
osh: 35000 so'm
manti: 30000 so'mHar aylanishda iterator bitta massiv (["osh", 35000]) beradi, destructuring esa uni ikki o'zgaruvchiga ochadi.
5.2 Satr — harflar bo'yicha, kod birliklari bo'yicha emas
Satr iteratori bitta nozik ishni to'g'ri qiladi:
const buyurtma = "choy🍵";
console.log(buyurtma.length); // 6
console.log([...buyurtma]); // [ 'c', 'h', 'o', 'y', '🍵' ]length — 6, lekin spread beshta belgi berdi. Emoji xotirada ikki "bo'lak"dan (kod birligidan) iborat, length esa bo'laklarni sanaydi. Satr iteratori esa bo'laklarni juftlab, butun belgi beradi. Shuning uchun emoji yoki kamyob harfli matnda belgilarni sanash uchun [...matn].length ishonchliroq.
5.3 Tekshiruvchi funksiya
"Bu qiymat iterable mi?" — degan savolga javob: Symbol.iterator kalitida funksiya bormi?
function iterablemi(qiymat) {
return (
qiymat != null &&
typeof qiymat[Symbol.iterator] === "function"
);
}
console.log(iterablemi(["osh"])); // true
console.log(iterablemi("Bahor")); // true
console.log(iterablemi(new Map())); // true
console.log(iterablemi({ osh: 35000 })); // false
console.log(iterablemi(42)); // false
console.log(iterablemi(null)); // falseqiymat != null — avval null va undefined ni chetlatadi: ularning xususiyatini o'qishning o'zi TypeError beradi. != bu yerda ataylab ikki belgili: u null ni ham, undefined ni ham ushlaydi. 42 uchun esa 42[Symbol.iterator] — undefined, xato chiqmaydi.
5.4 Oddiy obyektni qanday aylanamiz?
Obyekt iterable emas, chunki "nimani aylanish kerak?" degan savolga bitta javob yo'q: kalitlarnimi, qiymatlarnimi, juftliklarnimi? JavaScript taxmin qilmaydi — siz tanlaysiz. Obyekt bo'ylab yurish darsidagi uch metod massiv qaytaradi, massiv esa iterable:
const menyu = { osh: 35000, manti: 30000 };
for (const [taom, narx] of Object.entries(menyu)) {
console.log(taom, narx);
}Konsolda:
osh 35000
manti 30000Obyektni o'z iterable'ingizga aylantirish ham mumkin — [Symbol.iterator] metodini o'zingiz yozib. Buni keyingi darsda qilamiz.
Tekshirib ko'ring:
iterablemi(new Set([1, 2]).values())nima qaytaradi? Nega?
Javob
true. values() — iterator, va o'rnatilgan iteratorlarning hammasi o'zini qaytaradigan [Symbol.iterator]() metodiga ega. Demak ular bir vaqtda iterable ham. Shuning uchun for (const x of to'plam.values()) ishlaydi.
6. Iterator — bir martalik va "jonli"
6.1 Iterator bir marta o'qiladi
Massivni xohlagancha spread qilish mumkin: har safar yangi iterator yaratiladi. Iteratorni esa — faqat bir marta:
const taomlar = ["osh", "lag'mon", "manti"];
const iterator = taomlar.values();
console.log([...iterator]); // [ 'osh', "lag'mon", 'manti' ]
console.log([...iterator]); // []Ikkinchi spread bo'sh massiv berdi, xato esa chiqmadi. Xatcho'p allaqachon oxirgi betda — undan "keyingi bet" so'rasangiz, "tugadi" deydi. Qoida: iterable — qayta o'qiladi, iterator — bir martalik. Iteratorni o'zgaruvchiga saqlab, ikki joyda ishlatmang.
6.2 Iterator nusxa olmaydi
Massiv iteratori massivning nusxasini saqlamaydi. U faqat "qaysi o'rindaman" degan raqamni eslaydi va har next() da massivga qaytadan qaraydi:
const buyurtma = ["osh"];
const iterator = buyurtma.values();
buyurtma.push("somsa");
console.log([...iterator]); // [ 'osh', 'somsa' ]Iterator push dan oldin yaratilgan, lekin "somsa" ni ham berdi. Bu "jonli" bog'lanish kitob-xatcho'p o'xshatishiga ham mos: kimdir kitob oxiriga yangi bet tiksa, xatcho'p bilan o'qiyotgan odam unga ham yetib boradi.
Shu sabab for...of ichida aylanayotgan massivga element qo'shish xavfli. Har aylanishda bitta qo'shilsa, iterator hech qachon oxiriga yetmaydi — sikl cheksiz aylanadi. Callback imzosi darsidagi qoida bu yerda ham amal qiladi: aylanayotgan massivni o'zgartirmang.
6.3 Iterator oldindan hisoblamaydi
Yuqoridagi ikki misoldan muhim xulosa chiqadi: iterator qiymatni so'ralgan paytda oladi. U boshida "hamma qiymatlar ro'yxati"ni tayyorlab qo'ymaydi. Hozircha bu massiv ichidan olishdek oddiy ish. Lekin protokol qiymat qayerdan kelishini umuman so'ramaydi — next() uni massivdan ham olishi, hisoblab chiqarishi ham mumkin.
Tasavvur qiling: «Bahor»dagi navbat chiptalari apparati. Unda oldindan bosilgan million chipta yo'q — tugmani bosganingizda keyingi raqamni bosib beradi. Bu ham iterator: har "keyingisi" so'rovida yangi qiymat. U hech qachon "tugadi" demasligi ham mumkin. Bunday iteratordan [...x] bilan hammasini olishga urinsangiz, dastur abadiy kutib qoladi. for...of dagi break yoki ikki o'zgaruvchili destructuring esa bemalol ishlaydi — ular kerakligicha so'raydi.
Bunday "talab bo'yicha" iteratorlarni Generator funksiyalar darsida bir necha qatorda yozishni o'rganamiz. Bugungi darsning asosiy foydasi shu yerda: protokolni bilsangiz, generator ham, Map ham, NodeList ham bir xil tushuniladi.
Tekshirib ko'ring: Navbat chiptalari apparati kabi cheksiz iterator bor deylik.
const [a, b] = apparat;ishlaydimi?console.log([...apparat])chi?
Javob
Destructuring ishlaydi: ikki o'zgaruvchi — ikki next(), keyin to'xtaydi. Spread esa done: true ni kutadi, u esa hech qachon kelmaydi — dastur qotib qoladi. Spread, Array.from va oxirigacha boradigan for...of faqat tugaydigan iteratorlar uchun.
7. Ko'p uchraydigan xatolar
7.1 TypeError: menyu is not iterable
«Nega bu kerak?» bo'limidagi xato. Tarjimasi: "menyu — iterable emas". Xabarda sizning o'zgaruvchingiz nomi turadi. for...of, [...menyu] yoki const [a] = menyu — uchalasi ham shu xabarni beradi, chunki uchalasi ham menyu[Symbol.iterator] ni qidiradi va topmaydi.
Tuzatish: oddiy obyekt bo'lsa — Object.entries(menyu), Object.keys(menyu) yoki Object.values(menyu). Qiymat null yoki undefined bo'lib qolgan bo'lsa (masalan, funksiya hech narsa qaytarmagan) — xato boshqa joyda: o'zgaruvchiga qiymat qayerdan kelganini tekshiring.
7.2 new Map(obyekt) — boshqa so'zlar, bir xil sabab
const menyu = { osh: 35000 };
const xarita = new Map(menyu);Konsolda:
TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))Tarjimasi: "obyekt iterable emas (Symbol(Symbol.iterator) xususiyatini o'qib bo'lmadi)". Bu safar xabarda o'zgaruvchi nomi yo'q: Map konstruktori qiymatning turini ("object") aytadi, xolos. Qavs ichidagi qism aynan nimani qidirganini ochiq aytyapti — Symbol.iterator ni.
Tuzatish: new Map(Object.entries(menyu)) — entries juftliklar massivini beradi, Map aynan shuni kutadi.
7.3 Kvadrat va jingalak qavsni chalkashtirish
{ ...menyu } ishlagani uchun [...menyu] ham ishlaydi deb o'ylash. Birinchisi xususiyatlarni ko'chiradi, ikkinchisi iteratsiya protokolini ishlatadi. Teskarisi ham kutilmagan natija beradi: { ..."ab" } — { '0': 'a', '1': 'b' }, harflar indeks kalitli xususiyatlarga aylanadi.
7.4 Iteratorni ikki marta ishlatish
«Iterator bir marta o'qiladi» bo'limidagi holat. Eng yomoni — xato chiqmaydi, ikkinchi aylanish shunchaki bo'sh bo'ladi. Ma'lumot "yo'qolib qoldi" deb uzoq qidirasiz. Tuzatish: iterator emas, iterable'ni (massivni, Map ni) saqlang va kerak bo'lganda undan yangi iterator oling.
7.5 Array.from jim qoladi
const menyu = { osh: 35000 };
console.log(Array.from(menyu)); // []Array.from avval iterable'mi deb tekshiradi, bo'lmasa massivga o'xshashmi deb length ga qaraydi (Massivga o'xshash obyektlar). menyu da length yo'q — natija bo'sh massiv, xato yo'q. for...of darrov xato bergan joyda Array.from jim qoladi. Tuzatish: obyektni Object.entries bilan oching.
7.6 for...in ni for...of o'rniga yozish
const taomlar = ["osh", "manti"];
for (const t in taomlar) {
console.log(t, typeof t);
}Konsolda:
0 string
1 stringTaom nomlari o'rniga satr ko'rinishidagi indekslar chiqdi. for...in iteratsiya protokolini ishlatmaydi — u obyekt kalitlarini sanaydi (Obyekt bo'ylab yurish). Massivda kalitlar — "0", "1". Tuzatish: qiymatlar kerak bo'lsa — for...of. Eslab qolish uchun: in — indeks va kalitlar, of — qiymatlar.
8. Mashqlar
1-mashq (oson): Xatcho'pni qo'lda suring
Quyidagi kod nima chiqarishini avval qog'ozga yozing, keyin ishga tushirib tekshiring. Eslatma: Set har qiymatni bir marta saqlaydi — ikkinchi "osh" qo'shilmaydi.
const buyurtmalar = new Set(["osh", "manti", "osh"]);
const it = buyurtmalar.values();
console.log(it.next());
console.log(it.next());
console.log(it.next());Yechim
const buyurtmalar = new Set(["osh", "manti", "osh"]);
const it = buyurtmalar.values();
console.log(it.next());
console.log(it.next());
console.log(it.next());Konsolda:
{ value: 'osh', done: false }
{ value: 'manti', done: false }
{ value: undefined, done: true }Set da faqat ikki qiymat qoldi — takror "osh" tushib qoldi. Uchinchi next() "tugadi" dedi.
2-mashq (o'rta): Birinchi N tasi
birinchiN(iterable, n) funksiyasini yozing: u iterable'dan ko'pi bilan n ta qiymatni massivga olib qaytarsin. for...of ishlatmang — iteratorni [Symbol.iterator]() bilan oling va next() ni o'zingiz chaqiring (while sikli, «for...of ni qo'lda yozamiz» bo'limidagi kabi). Iterable n dan qisqa bo'lsa, borini qaytarsin.
console.log(birinchiN("Bahor", 3)); // [ 'B', 'a', 'h' ]Yechim
function birinchiN(iterable, n) {
const iterator = iterable[Symbol.iterator]();
const natija = [];
while (natija.length < n) {
const qadam = iterator.next();
if (qadam.done) {
break;
}
natija.push(qadam.value);
}
return natija;
}
console.log(birinchiN("Bahor", 3)); // [ 'B', 'a', 'h' ]
console.log(birinchiN(["osh"], 3)); // [ 'osh' ]
const menyu = new Map([["osh", 35000], ["manti", 30000]]);
console.log(birinchiN(menyu, 1)); // [ [ 'osh', 35000 ] ]Ikki shart siklni to'xtatadi: yetarli qiymat yig'ildi (natija.length < n yolg'on) yoki iterator tugadi (done). Funksiya n tadan ortiq next() chaqirmaydi. Shu xususiyat keyinroq juda muhim bo'ladi: Generator funksiyalar darsida cheksiz iteratorlarni ko'ramiz — ulardan [...x] bilan hammasini olib bo'lmaydi, birinchiN esa bemalol oladi.
3-mashq (qiyin): O'z for...of ingiz
harBiriUchun(iterable, callback) funksiyasini yozing. U for...of ning qo'lda yozilgan nusxasi bo'lsin: iterable'dan iterator olsin va har qiymat uchun callback(qiymat, tartibRaqami) ni chaqirsin (tartibRaqami 0 dan). for...of ishlatmang. So'ng uni massiv, satr va Map da sinang.
Yechim
function harBiriUchun(iterable, callback) {
const iterator = iterable[Symbol.iterator]();
let tartib = 0;
let qadam = iterator.next();
while (!qadam.done) {
callback(qadam.value, tartib);
tartib++;
qadam = iterator.next();
}
}
harBiriUchun(["osh", "manti"], (taom, i) => {
console.log(i, taom);
});
harBiriUchun("ok", (harf) => console.log(harf));
const menyu = new Map([["choy", 5000]]);
harBiriUchun(menyu, ([taom, narx]) => {
console.log(`${taom} — ${narx}`);
});Konsolda:
0 osh
1 manti
o
k
choy — 5000Map uchun callback parametrida destructuring: har qiymat [kalit, qiymat] massivi. Nima uchun "indeks" emas, "tartib raqami"? Chunki satr, Map yoki Set da indeks degan narsa yo'q — iterator faqat "navbatdagisi"ni biladi. Tartibni sanashni biz o'zimiz qildik.
4-mashq: Vazifalar qadami — royxat iterable mi? (mashqlar repo)
Bu dars vazifalar ilovasini o'zgartirmaydi — qadam mashqlar reposida va u keyingi darsga tayyorgarlik. kurs/mashqlar/10/50-iteratsiya/royxat.js faylini yarating.
OOP tamoyillari va kompozitsiya darsida VazifalarRoyxati ni yasagan edingiz: vazifalar #vazifalar massivida yashirin. Faylga klassning qisqa nusxasini ko'chiring (Vazifa — faqat id, matn, bajarildi; royxat — #vazifalar, static dan, korinadiganlar(filtr)). So'ng shu darsdagi iterablemi bilan tekshiring:
royxatiterable mi?royxat.korinadiganlar("hammasi")chi?- Barcha vazifa matnlarini chiqarish uchun hozir qanday yozish kerak?
Yechim
class Vazifa {
constructor(id, matn, bajarildi = false) {
this.id = id;
this.matn = matn;
this.bajarildi = bajarildi;
}
}
class VazifalarRoyxati {
#vazifalar = [];
static dan(malumotlar) {
const yangi = new VazifalarRoyxati();
yangi.#vazifalar = malumotlar.map(
(m) => new Vazifa(m.id, m.matn, m.bajarildi),
);
return yangi;
}
korinadiganlar(filtr) {
if (filtr === "hammasi") {
return this.#vazifalar.filter(() => true);
}
const bajarildimi = filtr === "bajarilgan";
return this.#vazifalar.filter(
(v) => v.bajarildi === bajarildimi,
);
}
}
function iterablemi(qiymat) {
return (
qiymat != null &&
typeof qiymat[Symbol.iterator] === "function"
);
}
const royxat = VazifalarRoyxati.dan([
{ id: 1, matn: "Non olish", bajarildi: false },
{ id: 2, matn: "DOM darsini takrorlash", bajarildi: true },
]);
console.log(iterablemi(royxat)); // false
console.log(iterablemi(royxat.korinadiganlar("hammasi"))); // true
for (const v of royxat.korinadiganlar("hammasi")) {
console.log(v.matn);
}Konsolda:
false
true
Non olish
DOM darsini takrorlashRoyxatning o'zi iterable emas: for (const v of royxat) yozsangiz — TypeError: royxat is not iterable. Barcha vazifani aylanish uchun hozir korinadiganlar("hammasi") ni chaqirish kerak. Bu ikki noqulaylik beradi: birinchidan, nom ma'noga mos emas ("ko'rinadiganlar" emas, "hammasi" kerak); ikkinchidan, filter har safar butun massivning nusxasini yasaydi — faqat aylanib chiqish uchun.
Keyingi darsda VazifalarRoyxati ga [Symbol.iterator]() qo'shamiz. Shunda for (const v of holat.vazifalar) to'g'ridan-to'g'ri ishlaydi, #vazifalar esa baribir yashirin qoladi.
Commit (mashqlar repo): 10/50: VazifalarRoyxati iterable emasligini tekshir.
9. Real ishda
- Har kuni, sezilmasdan. Har
for...of, har[...massiv, yangi]va har destructuring — iteratsiya protokoli. React'da (interfeys yasash kutubxonasi, 17-qismda o'rganamiz) holatni yangilashda —[...vazifalar, yangiVazifa]— ham shu ishlaydi. - Brauzer API'lari.
NodeList,FormData(for (const [nom, qiymat] of formData)),URLSearchParams— hammasi iterable. Ularning har biri uchun alohida "aylanish usuli"ni yodlash shart emas: protokol bitta. - Katta ma'lumot. Node.js'da faylni qatorma-qator o'qish, serverdan sahifalab ma'lumot olish — iteratorlar ustiga qurilgan. Faqat u yerda qiymatlar vaqt o'tib keladi; buni Asinxron iteratsiya darsida ko'rasiz.
- Intervyu. "Iterable va iterator farqi nima?", "Nega oddiy obyekt
for...ofbilan ishlamaydi?", "for...invafor...offarqi?" — ko'p beriladigan savollar. Endi javobingiz "Symbol.iteratorvanext()" bilan boshlanadi.
Xulosa
- Iterable —
[Symbol.iterator]()metodi bor obyekt; iterator —next()metodi bor obyekt. next()har safar{ value, done }qaytaradi;done: true— qiymatlar tugadi.for...of, spread, massiv destructuring,Array.from,new Map/Set— hammasi shu protokol. Destructuring faqat kerakligichanext()chaqiradi.- Massiv, satr,
Map,Set,NodeList— iterable; oddiy obyekt — yo'q (Object.entriesbilan oching). - Iterable qayta o'qiladi, iterator — bir martalik. Massiv iteratori nusxa olmaydi, massivni "jonli" ko'radi.
{ ...x }— xususiyatlar,[...x]— protokol.
Keyingi dars: O'z iterable obyektingizni yasash — [Symbol.iterator]() va next() ni o'zingiz yozib, istalgan obyektni for...of bilan aylanadigan qilasiz.
Manbalar
- MDN: "Iteration protocols" — developer.mozilla.org
- MDN: "for...of", "Symbol.iterator" — developer.mozilla.org
- ECMAScript spetsifikatsiyasi: "Iteration" bo'limi — tc39.es/ecma262
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!