IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur10/65-dars22 daqiqa
Mundarija (34)

JavaScript massivga o'xshash obyektlar va teshikli massivlar: arguments, NodeList va bo'sh kataklar

Qisqacha: Massivga o'xshash obyekt (array-like) — indeksli elementlari va length i bor, lekin map, filter kabi metodlari yo'q obyekt: arguments, HTMLCollection, { 0: "osh", length: 1 }. Uni haqiqiy massivga Array.from(x) aylantiradi. Teshikli massiv esa — ichida bo'sh katagi bor massiv ([1, , 3]); metodlar teshikni har xil ko'radi, shuning uchun teshik yaratmang.

Bu darsda

  • Massivga o'xshash obyektni haqiqiy massivdan ajrata olasiz va Array.isArray bilan tekshira olasiz.
  • arguments, NodeList, HTMLCollection ni Array.from yoki spread bilan massivga aylantira olasiz.
  • Iterable va massivga o'xshash farqini va qachon spread ishlamasligini tushuntira olasiz.
  • Teshik qanday paydo bo'lishini va qaysi metod uni qanday ko'rishini bilasiz.
  • length ga yozish va delete arr[i] tuzoqlarini taniysiz.

Oldin bilishingiz kerak: flat, flatMap, fill va Array.from, Metod zanjiri, Element qo'shish va olib tashlash, Parametrlar va arguments, Elementlarni tanlash.

1. Nega bu kerak?

09-qismda bu xatoni ko'rgan edingiz (DOM bo'ylab harakatlanish):

text
Uncaught TypeError: menyu.children.forEach is not a function

menyu.children ichida elementlar bor edi. Ularni indeks bilan olish mumkin edi, length ham bor edi. Lekin forEach yo'q chiqdi. O'shanda "for...of ishlating" dedik va o'tib ketdik. Bugun sababini ochamiz.

JavaScript'da "ro'yxatga o'xshab ko'rinadigan" narsalar ko'p. Ularning hammasi ham massiv emas. Bir xili — haqiqiy massiv. Boshqasi esa faqat massivga o'xshaydi: raqamli kataklari va uzunligi bor, xolos.

Hayotdan o'xshatish: «Bahor»dagi qog'oz navbat ro'yxati. Unda raqamlar bor (1, 2, 3...) va oxirida "jami: 5 kishi" deb yozilgan. Lekin bu oddiy qog'oz — elektron navbat tizimidagi "saralash" yoki "qidirish" tugmalari unda yo'q. Tugmalar kerak bo'lsa, ro'yxatni tizimga ko'chirib kiritasiz. Massivga o'xshash obyektni ham avval haqiqiy massivga ko'chirasiz.

Darsning ikkinchi qismi — teshikli massivlar. Massiv yaratish va Element qo'shish darslarida <3 empty items> degan g'alati yozuvni ko'rgan edik va "teshiklar keyin kutilmagan natija beradi" deb va'da qilgan edik. Bugun shu kutilmagan natijalarni o'z ko'zimiz bilan ko'ramiz — va ulardan qanday qochishni o'rganamiz.

2. Massivga o'xshash obyekt

2.1 O'zimiz yasaymiz

Massivga o'xshash obyektni qo'lda yasash mumkin. Kalitlari — 0, 1, 2..., yana bitta length xususiyati:

js
const navbat = { 0: "Ali", 1: "Malika", 2: "Bobur", length: 3 };

console.log(navbat[1]); // Malika
console.log(navbat.length); // 3
console.log(Array.isArray(navbat)); // false

Indeks bilan o'qish ishladi, uzunlik ham bor. Lekin Array.isArray — "bu haqiqiy massivmi?" degan savolga — false dedi. Obyekt kalitlari doim satr, navbat[1] esa navbat["1"] ning o'zi (Obyekt: kalit-qiymat juftliklari). Ya'ni bu shunchaki kalitlari raqamga o'xshagan oddiy obyekt.

Massivga o'xshash obyekt (array-like object) — ikki shartga javob beradigan har qanday obyekt: unda length bor va elementlari 0, 1, 2... kalitlarida turadi. Massiv metodlari (map, filter, forEach) unda yo'q.

Metod chaqirsangiz:

js
const navbat = { 0: "Ali", 1: "Malika", length: 2 };

navbat.map((ism) => ism.toUpperCase());
text
TypeError: navbat.map is not a function

Tarjimasi: "navbat.map — funksiya emas". navbat da map degan xususiyat umuman yo'q — undefined. undefined ni chaqirib bo'lmaydi. Xato map qatorida chiqqani to'g'ri: muammo obyektda emas, unda kutgan metodingiz yo'qligida.

2.2 Qayerda uchraydi?

Massivga o'xshash obyektni qo'lda kam yasaysiz. Lekin tayyorlari bilan har kuni uchrashasiz:

Nima Qayerdan keladi forEach bormi?
arguments oddiy function ichida (Parametrlar) yo'q
HTMLCollection children, getElementsByClassName yo'q
NodeList querySelectorAll, childNodes bor
Satr "salom" yo'q (lekin [0] va length bor)
Typed array Typed arrays darsida bor

NodeList — o'rtadagi holat: brauzer unga forEach ni qo'shib bergan, lekin map, filter, reduce yo'q. Shuning uchun Callback imzosi darsida vazifalar dagi filtr tugmalarini forEach bilan aylantira oldik, map bilan esa aylantira olmasdik.

arguments ni Parametrlar darsida "ro'yxatga o'xshaydi, lekin to'liq ro'yxat emas" degan edik. Mana isboti:

js
function narxlarniKor() {
  console.log(arguments);
  console.log(Array.isArray(arguments));
  console.log(typeof arguments.map);
}

narxlarniKor(35000, 28000);

Konsolda:

text
[Arguments] { '0': 35000, '1': 28000 }
false
undefined

Node arguments ni [Arguments] { '0': ..., '1': ... } deb ko'rsatdi — massiv qavsi [ ] emas, obyekt qavsi { }. Kalitlar qo'shtirnoqda, ya'ni satr. Ichki tuzilish konsolning o'zida ko'rinib turibdi.

Tekshirib ko'ring: const x = { 0: "osh", length: 1 }; — x.length va Array.isArray(x) nima qaytaradi? x.push("manti") chi?

Javob

1 va false. x.push(...) esa TypeError: x.push is not a function beradi — push ham massiv metodi, oddiy obyektda u yo'q. length — x ning oddiy xususiyati, uni biz o'zimiz yozdik.

3. Haqiqiy massivga aylantirish

3.1 Array.from

flat, fill va Array.from darsida Array.from ni ko'rgan edik: "shundan massiv yasa". U massivga o'xshash obyektni ham qabul qiladi:

js
const navbat = { 0: "Ali", 1: "Malika", 2: "Bobur", length: 3 };

const massiv = Array.from(navbat);

console.log(massiv); // [ 'Ali', 'Malika', 'Bobur' ]
console.log(Array.isArray(massiv)); // true
console.log(massiv.map((ism) => ism.length)); // [ 3, 6, 5 ]

Array.from length ni o'qiydi va 0 dan length - 1 gacha har kalitni oladi. Natija — yangi, haqiqiy massiv. Endi unda map ham, filter ham bor.

Ikkinchi argument — har elementni aylantiradigan callback. U map ni alohida chaqirishdan qutqaradi:

js
function jamiHisobla() {
  const narxlar = Array.from(arguments, (x) => Number(x));
  return narxlar.reduce((jami, n) => jami + n, 0);
}

console.log(jamiHisobla("35000", "28000", 5000)); // 68000

arguments dagi satrlar songa aylandi, keyin reduce ularni yig'di.

3.2 Spread: faqat iterable bo'lsa

[...x] ham massiv yasaydi. Lekin u boshqa qoidaga tayanadi. for...of darsida iterable atamasini ko'rgan edik — "ichidagi qiymatlarni birma-bir bera oladigan narsa". Spread faqat iterable bilan ishlaydi:

js
const navbat = { 0: "Ali", 1: "Malika", length: 2 };

console.log([...navbat]);
text
TypeError: navbat is not iterable

Tarjimasi: "navbat — iterable emas". Qo'lda yasalgan massivga o'xshash obyektda length bor, lekin "birma-bir berish" qobiliyati yo'q. Array.from esa ikkalasini ham tushunadi: avval iterable'mi deb tekshiradi, bo'lmasa length va indekslarga qaraydi.

Tayyor brauzer to'plamlari va arguments — ikkalasi ham: massivga o'xshash va iterable. Shuning uchun ular bilan spread ham ishlaydi:

js
function ismlar() {
  return [...arguments];
}

console.log(ismlar("Ali", "Malika")); // [ 'Ali', 'Malika' ]

Ikki tushunchani jadvalda solishtiring:

Massivga o'xshash Iterable
Nima kerak length + 0, 1, 2... kalitlar birma-bir berish qobiliyati
Array.from(x) ishlaydi ishlaydi
[...x], for...of ishlamaydi ishlaydi
Misol { 0: "a", length: 1 } arguments, NodeList, satr

"Birma-bir berish" qobiliyati ichida qanday ishlashini Iteratsiya protokoli darsida ochamiz — hozir bilish shart emas.

Qaysi usulni tanlash kerakligini sxemada kuzating — har savolda chap tomon "ha", o'ng tomon "yo'q":

flowchart TD
  A{"Array.isArray(x)?"} -- "ha" --> M["Massiv: metodlar tayyor"]
  A -- "yo'q" --> I{"for...of bilan aylanadimi?"}
  I -- "ha, iterable" --> S["[...x] yoki Array.from(x)"]
  I -- "yo'q" --> L{"length bormi?"}
  L -- "ha" --> F["Faqat Array.from(x)"]
  L -- "yo'q" --> O["Massiv emas: Object.values(x)"]

Oxirgi tarmoq — oddiy obyekt, masalan { osh: 35000 }. Uning qiymatlari kerak bo'lsa — Obyekt bo'ylab yurish darsidagi Object.values.

Maslahat: Eski kodlarda Array.prototype.slice.call(arguments) degan yozuvni uchratasiz. Bu — Array.from paydo bo'lishidan (2015) oldingi usul: massivning slice metodini massivga o'xshash obyektga "qarzga berish". call ni call, apply, bind darsida o'rganamiz. Yangi kodda — Array.from yoki [...x].

3.3 Brauzerda: HTMLCollection va NodeList

Endi 09-qismdagi xatoni tuzatamiz. Natijani oching va konsol paneliga qarang — ikkala to'plamda nima bor, nima yo'q:

html
<ul id="menyu">
  <li>Osh</li>
  <li>Lag'mon</li>
  <li>Manti</li>
</ul>
<script>
  const menyu = document.getElementById("menyu");
  const bolalar = menyu.children;
  const topilgan = menyu.querySelectorAll("li");

  console.log(Array.isArray(bolalar), Array.isArray(topilgan));
  console.log(typeof bolalar.forEach, typeof topilgan.forEach);
  console.log(typeof topilgan.map);

  const nomlar = Array.from(bolalar, (li) => li.textContent);
  console.log(nomlar);
  const uzunlar = [...topilgan].filter(
    (li) => li.textContent.length > 4,
  );
  console.log(uzunlar);
</script>

Konsolda:

text
false false
undefined function
undefined
(3) ['Osh', "Lag'mon", 'Manti']
(2) [li, li]

Ikkala to'plam ham massiv emas. HTMLCollection da forEach yo'q, NodeList da bor, map esa ikkalasida ham yo'q. Array.from va spread esa ikkalasini ham haqiqiy massivga aylantirdi — endi filter va map ishlaydi.

Brauzer konsoli massivni (3) ['Osh', ...] deb ko'rsatadi: oldida uzunlik qavsda. Node'da xuddi shu massiv [ 'Osh', "Lag'mon", 'Manti' ] ko'rinadi — ma'nosi bir xil.

Diqqat: HTMLCollection jonli — DOM o'zgarsa, u ham o'zgaradi (Elementlarni tanlash). Array.from(menyu.children) esa o'sha lahzadagi suratni oladi: keyin qo'shilgan li massivga tushmaydi. Elementlarni aylanib o'chirish kerak bo'lsa, bu aynan foydali — massiv sikl davomida qisqarmaydi.

Tekshirib ko'ring: document.querySelectorAll("li").map(...) nima beradi? Uni qanday tuzatasiz?

Javob

TypeError: document.querySelectorAll(...).map is not a function. NodeList da forEach bor, map esa yo'q. Tuzatish: Array.from(document.querySelectorAll("li"), (li) => ...) yoki [...document.querySelectorAll("li")].map(...).

4. Teshikli massivlar

4.1 Teshik qanday paydo bo'ladi

Teshik (hole) — massivda indeksi bor, lekin unga hech qachon qiymat yozilmagan katak. Teshikli massiv (sparse array) — shunday katagi bor massiv. Teshik undefined qiymatli katakdan farq qiladi: unda qiymat umuman yo'q.

Teshik besh xil yo'l bilan paydo bo'ladi:

js
const a = [1, , 3]; // ikki vergul orasi bo'sh
const b = ["non"];
b[3] = "sut"; // uzoq indeksga yozish
const c = [1, 2, 3];
delete c[1]; // delete operatori
const d = new Array(3); // uzunlik bilan yaratish
const e = ["osh"];
e.length = 3; // length ni oshirish

console.log(a); // [ 1, <1 empty item>, 3 ]
console.log(b); // [ 'non', <2 empty items>, 'sut' ]
console.log(c); // [ 1, <1 empty item>, 3 ]
console.log(d); // [ <3 empty items> ]
console.log(e); // [ 'osh', <2 empty items> ]

Node teshikni <1 empty item> — "1 ta bo'sh element" deb ko'rsatadi. Chrome konsoli [1, empty, 3] deydi — ma'nosi bir xil.

Teshikni undefined dan ajratishning aniq yo'li — in operatori. U "shu kalit obyektda bormi?" deb so'raydi:

js
const teshikli = [1, , 3];
const undefinedli = [1, undefined, 3];

console.log(teshikli[1], undefinedli[1]); // undefined undefined
console.log(1 in teshikli); // false
console.log(1 in undefinedli); // true

Ikkalasida ham [1] o'qilganda undefined chiqdi. Lekin in farqni ko'rsatdi: birinchisida 1 kaliti umuman yo'q. Massiv ham obyekt — teshik esa kaliti yo'q indeks.

Hayotdan o'xshatish: kinoteatr qatori. undefined — o'rindiq bor, lekin hozir bo'sh. Teshik — o'rindiqning o'zi olib tashlangan, raqami esa qatorda qolgan. Ikkalasida ham "hech kim o'tirmagan". Lekin farrosh bo'sh o'rindiqni artadi, o'rindiq yo'q joyni esa o'tib ketadi.

4.2 Metodlar teshikni har xil ko'radi

Teshikning asosiy xavfi shu. Bir xil massiv, uch xil yo'l:

js
const r = [10, , 30];

r.forEach((x, i) => console.log("forEach", i, x));
for (const x of r) {
  console.log("for...of", x);
}
console.log(r.map((x) => x * 2));

Konsolda:

text
forEach 0 10
forEach 2 30
for...of 10
for...of undefined
for...of 30
[ 20, <1 empty item>, 60 ]

forEach teshikni o'tkazib yubordi — callback faqat ikki marta chaqirildi. for...of esa teshikni undefined deb berdi. map callback'ni teshikda chaqirmadi va natijada ham teshik qoldirdi.

Qoida ikkita guruhga bo'linadi:

Teshikni o'tkazib yuboradi Teshikni undefined deb ko'radi
forEach, map, filter for...of, spread [...r]
reduce, some, every Array.from, find, findIndex
Object.keys, for...in includes, at, entries
sort (teshiklarni oxiriga suradi) toSorted, toReversed, with

Jadvalni yodlash shart emas. Asosiy xulosa: bir xil teshikli massiv bilan ikki xil kod ikki xil javob beradi. Eski (ES5, 2009) metodlar teshikni o'tkazib yuboradi. Yangiroqlarining ko'pi (ES2015 dan keyingilari) uni undefined deb ko'radi. indexOf esa o'tkazib yuboradi, includes — yo'q: [1, , 3].indexOf(undefined) — -1, [1, , 3].includes(undefined) — true.

Bitta foydali tomoni bor: teshikli massivni filter dan o'tkazsangiz, teshiklar yo'qoladi. toSorted, with kabi nusxa bilan o'zgartiradigan metodlar esa teshikni undefined ga aylantiradi — natijada teshik qolmaydi.

4.3 Real xato: hafta o'rtachasi

Jasur aka haftalik tushumni yozib boradi. Payshanba kuni Otabek kassaga yozishni unutdi — u yerda teshik qoldi:

js
const tushum = [];
tushum[0] = 1200000;
tushum[1] = 1500000;
tushum[3] = 1800000; // 2-indeks — payshanba — yozilmadi

const jami = tushum.reduce((s, x) => s + x, 0);
console.log(jami); // 4500000
console.log(jami / tushum.length); // 1125000

let siklJami = 0;
for (const x of tushum) {
  siklJami += x;
}
console.log(siklJami); // NaN

Bitta massiv, ikki natija — va ikkalasi ham noto'g'ri:

  • reduce teshikni o'tkazib yubordi va yig'indi 4 500 000 chiqdi. Lekin length — 4: teshik ham sanaldi. O'rtacha 1 125 000 — haqiqiydan past. Uch kun ishlab, to'rtga bo'lindi.
  • for...of teshikni undefined deb berdi. 1200000 + 1500000 + undefined — NaN (Number turi). Bu yaxshiroq: hech bo'lmasa xato ko'rinib turibdi.

Eng xavflisi — birinchi holat. Raqam ishonarli ko'rinadi, xato xabari yo'q. reduce qaysi kunlarni ko'rganini qadamma-qadam kuzating:

Tuzatish — teshikni ma'noli qiymatga aylantirish va bu haqda ochiq qaror qabul qilish. "Yozilmagan kun" — 0 mi yoki hisobga olinmaydimi? Bu dasturchi emas, Jasur aka hal qiladigan savol. Ikkala variant:

js
const tushum = [];
tushum[0] = 1200000;
tushum[1] = 1500000;
tushum[3] = 1800000;

// 1-variant: yozilmagan kun — 0 so'm
const nollar = Array.from(tushum, (x) => x ?? 0);
console.log(nollar); // [ 1200000, 1500000, 0, 1800000 ]

// 2-variant: yozilmagan kun hisobga olinmaydi
const yozilgan = tushum.filter(() => true);
const jami = yozilgan.reduce((s, x) => s + x, 0);
console.log(jami / yozilgan.length); // 1500000

Array.from teshikni undefined deb beradi, ?? 0 esa uni 0 qiladi (?? operatori). filter(() => true) hamma elementni oladi, lekin teshik callback'ga umuman kelmaydi — shuning uchun yangi massivda u yo'q.

Tekshirib ko'ring: [5, , 5].reduce((s, x) => s + 1, 0) nima qaytaradi? Nega 3 emas?

Javob

2. reduce teshikda callback'ni chaqirmaydi. Callback faqat ikki marta ishladi — ikki haqiqiy element uchun. Massiv uzunligi 3 bo'lsa ham, "elementlar soni" 2. Teshikli massivda length elementlar sonini aytmaydi.

5. length — oddiy xususiyat emas

5.1 length qanday hisoblanadi

Massivning length i — "elementlar soni" emas, aniqrog'i "eng katta indeks + 1". Teshiksiz massivda bu ikkisi bir xil. Teshikli massivda — farqli:

js
const b = ["non"];
b[99] = "sut";

console.log(b.length); // 100
console.log(Object.keys(b).length); // 2

Ikki element, lekin length — 100. Object.keys haqiqiy kalitlarni sanadi.

5.2 length ga yozish

length ni o'qibgina qolmay, unga yozish ham mumkin. Kamaytirsangiz — massiv qisqaradi, ortiqcha elementlar o'chadi. Oshirsangiz — oxirida teshiklar paydo bo'ladi:

js
const taomlar = ["osh", "lag'mon", "manti", "ko'k choy"];

taomlar.length = 2;
console.log(taomlar); // [ 'osh', "lag'mon" ]

taomlar.length = 4;
console.log(taomlar); // [ 'osh', "lag'mon", <2 empty items> ]

Uzunlik qaytdi, lekin "manti" va "ko'k choy" qaytmadi — ular o'chib ketgan. O'rnida faqat teshiklar.

massiv.length = 0 — massivni tozalashning tanish usuli. U shu massivning o'zini bo'shatadi. Unga boshqa o'zgaruvchi ham qarasa, u ham bo'sh massivni ko'radi (Havola semantikasi). Ba'zan aynan shu kerak, ba'zan — bu xato. massiv = [] esa faqat bitta o'zgaruvchiga yangi massiv beradi (u let bo'lishi kerak), boshqalari eskisini ko'raveradi.

5.3 delete — yana bir bor

Element qo'shish va olib tashlash darsida delete savat[1] tuzog'ini ko'rgan edik. Endi u nega xavfli ekani aniq: delete teshik yaratadi, length esa o'zgarmaydi.

js
const savat = ["non", "sut", "tuxum"];
delete savat[1];

console.log(savat.length); // 3
const katta = savat.map((m) => m.toUpperCase());
console.log(katta); // [ 'NON', <1 empty item>, 'TUXUM' ]
console.log(savat.join(", ")); // non, , tuxum

Chekda "non, , tuxum" — ikki vergul orasida bo'sh joy. Xato yo'q, lekin mijoz g'alati chek oladi. To'g'risi — splice(1, 1) (asl massivni o'zgartiradi) yoki toSpliced(1, 1) va filter (yangi massiv qaytaradi, Nusxa bilan o'zgartirish).

6. Teshiksiz massiv yasash

6.1 new Array(n) tuzog'i

1 dan 5 gacha raqamlar kerak. Mantiqan to'g'ri ko'rinadigan kod:

js
const raqamlar = new Array(5).map((_, i) => i + 1);

console.log(raqamlar); // [ <5 empty items> ]

Hech narsa o'zgarmadi! new Array(5) — beshta teshik. map esa teshikda callback chaqirmaydi va teshikni o'z o'rnida qoldiradi. Natija — yana beshta teshik.

Teshiksiz variantlar (Array.from darsidan):

js
console.log(Array.from({ length: 5 }, (_, i) => i + 1));
console.log(new Array(5).fill(0));
console.log([...new Array(3)]);

Konsolda:

text
[ 1, 2, 3, 4, 5 ]
[ 0, 0, 0, 0, 0 ]
[ undefined, undefined, undefined ]

Array.from({ length: 5 }, ...) — massivga o'xshash obyektdan massiv. { length: 5 } da kalitlar yo'q, lekin Array.from har indeksni o'qiydi va undefined oladi — callback esa har birida ishlaydi. fill har katakka qiymat yozadi — teshiklar yo'qoladi. Spread teshikni undefined qiladi.

Maslahat: new Array(3) va Array.of(3) ni adashtirmang. Bitta son berilsa, new Array(3) uni uzunlik deb oladi va uchta teshik yasaydi. Array.of(3) esa [3] — bitta elementli massiv. Odatda ikkalasi ham kerak emas: [3] yoki Array.from({ length: 3 }) yozing.

6.2 Tezlik haqida bir gap

JavaScript dvigateli V8 (Chrome va Node ichidagi) massivlarni ichki "turlarga" ajratadi. Massivda bir marta teshik paydo bo'lsa, V8 uni "teshikli" turga o'tkazadi. Har o'qishda u endi qo'shimcha tekshiradi: "bu katakda qiymat bormi?". Teshik keyin to'ldirilsa ham, massiv eski, tezroq turiga qaytmaydi (manba: v8.dev, "Elements kinds in V8").

Oddiy dasturda bu farqni sezmaysiz. Lekin o'n minglab elementli hisob-kitoblarda sezilishi mumkin. Chuqurroq — Hidden classes va inline caching darsida. Xulosa o'sha: massivni teshiksiz yarating — push, Array.from, fill bilan.

Tekshirib ko'ring: Array.from({ length: 3 }) va new Array(3) — qaysi birida teshik bor? [...] bilan tekshirmasdan, in bilan qanday bilasiz?

Javob

new Array(3) da — uchta teshik. Array.from({ length: 3 }) esa [ undefined, undefined, undefined ] — qiymatlari bor kataklar. Tekshirish: 0 in new Array(3) — false, 0 in Array.from({ length: 3 }) — true.

7. Ko'p uchraydigan xatolar

7.1 children.forEach is not a function

HTMLCollection da forEach yo'q. Tuzatish: for...of, yoki Array.from(el.children).forEach(...). querySelectorAll dan kelgan NodeList da esa forEach bor, lekin map/filter yo'q — u holda ham Array.from.

7.2 Qo'lda yasalgan obyektni spread qilish

[...{ 0: "a", length: 1 }] — TypeError: ... is not iterable. Spread iterable talab qiladi. Tuzatish: Array.from(x).

7.3 new Array(n).map(...) bo'sh qoladi

Callback bir marta ham chaqirilmaydi, natija — yana teshiklar. Tuzatish: Array.from({ length: n }, (_, i) => ...).

7.4 O'rtacha yoki sanoq length ga tayanadi

Teshikli massivda length elementlar soni emas. reduce teshikni o'tkazadi, length esa sanaydi — natija jimgina buziladi. Tuzatish: teshikni oldindan aniq qiymatga aylantiring (Array.from(r, (x) => x ?? 0)) yoki filter bilan olib tashlang.

7.5 delete massiv[i]

Teshik yaratadi, length o'zgarmaydi, join da bo'sh joy qoladi. Tuzatish: splice, toSpliced yoki filter.

8. Mashqlar

1-mashq (oson): arguments dan massiv

engArzoni funksiyasi eski uslubda yozilgan — parametrlar o'rniga arguments ishlatadi. Uni o'zgartirmasdan, ichida arguments ni massivga aylantirib, eng kichik narxni toping. Ishora: massivga aylantirgach, reduce yoki Math.min(...massiv) (Math obyekti).

js
function engArzoni() {
  // shu yerga yozing
}

console.log(engArzoni(35000, 28000, 30000, 5000));
Yechim
js
function engArzoni() {
  const narxlar = Array.from(arguments);
  return Math.min(...narxlar);
}

console.log(engArzoni(35000, 28000, 30000, 5000)); // 5000

Math.min(...arguments) ham ishlaydi — arguments iterable. Lekin Array.from niyatni aniq aytadi: "endi massiv bilan ishlayman". Yangi kodda esa arguments o'rniga rest parametr yozing: function engArzoni(...narxlar) — u boshidan haqiqiy massiv (Spread va rest).

2-mashq (o'rta): Teshiklarni sanash

teshiklarSoni(massiv) funksiyasini yozing: massivdagi teshiklar sonini qaytarsin. undefined qiymatli katak teshik emas. Ishora: in operatori yoki Object.keys(massiv).length — haqiqiy kalitlar soni.

js
console.log(teshiklarSoni([1, , 3, , 5]));      // 2
console.log(teshiklarSoni([1, undefined, 3]));  // 0
console.log(teshiklarSoni(new Array(4)));        // 4
Yechim
js
function teshiklarSoni(massiv) {
  return massiv.length - Object.keys(massiv).length;
}

console.log(teshiklarSoni([1, , 3, , 5])); // 2
console.log(teshiklarSoni([1, undefined, 3])); // 0
console.log(teshiklarSoni(new Array(4))); // 4

length — teshiklar bilan birga sanaladi. Object.keys — faqat haqiqiy kalitlar. Farqi — teshiklar soni. in bilan ham yozsa bo'ladi: 0 dan length - 1 gacha sikl, har i in massiv false bo'lsa — sanoqni oshiring. Lekin reduce yoki filter bilan sanab bo'lmaydi — ular teshikni ko'rmaydi!

3-mashq (qiyin): Har qanday "ro'yxat"dan massiv

royxatgaAylantir(x) funksiyasini yozing. U uch holatni to'g'ri hal qilsin:

  • x — massiv: uning nusxasini qaytarsin, teshiklarni null ga almashtirib;
  • x — massivga o'xshash yoki iterable (arguments, { 0: "a", length: 1 }, satr): massivga aylantirsin;
  • boshqa holat (son, null, oddiy obyekt length siz): bo'sh massiv qaytarsin.

Ishora: Array.from iterable'ni ham, length li obyektni ham tushunadi. Son va null uchun avval typeof va null tekshiruvi (typeof).

Yechim
js
function royxatgaAylantir(x) {
  if (Array.isArray(x)) {
    return Array.from(x, (el) => el ?? null);
  }
  if (x === null || x === undefined) {
    return [];
  }
  if (typeof x === "string" || typeof x.length === "number") {
    return Array.from(x);
  }
  return [];
}

function argumentlar() {
  return arguments;
}

console.log(royxatgaAylantir([1, , 3]));
console.log(royxatgaAylantir({ 0: "osh", 1: "manti", length: 2 }));
console.log(royxatgaAylantir(argumentlar("Ali", "Bobur")));
console.log(royxatgaAylantir("non"));
console.log(royxatgaAylantir(42));
console.log(royxatgaAylantir({ osh: 35000 }));

Konsolda:

text
[ 1, null, 3 ]
[ 'osh', 'manti' ]
[ 'Ali', 'Bobur' ]
[ 'n', 'o', 'n' ]
[]
[]

Ikki nozik joy. Birinchi if dagi Array.from(x, ...) — massivni ham nusxalaydi: callback teshikda undefined oladi va ?? null uni null qiladi. x.map(...) bunday qila olmasdi — teshikni o'tkazib yuborardi. Ikkinchisi: null tekshiruvi x.length dan oldin turishi shart, aks holda null.length — TypeError. Uchinchi if da satrni alohida tekshirdik: satrda length bor, lekin typeof uni "string" deydi.

4-mashq: Mashqlar qadami — teshikka chidamli tushum hisoboti

Bu dars mavzusi vazifalar ilovasiga tegmaydi — qadam mashqlar reposida (Yaxshi commit darsida ochilgan). 10-qism mashqlari — 10/ papkasida, har dars o'z papkasida.

kurs/mashqlar/10/10-teshikli-massivlar/tushum.js faylini yarating. Unda hisobot(tushum) funksiyasi bo'lsin. U kunlik tushum massivini oladi (ichida teshik bo'lishi mumkin) va obyekt qaytaradi:

  • kunlar — haftaning nechta kuni (massiv uzunligi);
  • yozilgan — nechta kun haqiqatan yozilgan;
  • yozilmagan — yozilmagan kunlar raqamlari (1 dan boshlab);
  • ortacha — faqat yozilgan kunlar bo'yicha o'rtacha.

Sinov uchun «Real xato: hafta o'rtachasi» bo'limidagi tushum massivini ishlating.

Yechim
js
function hisobot(tushum) {
  const kunlar = Array.from(tushum, (summa, i) => ({
    kun: i + 1,
    summa,
  }));
  const yozilgan = kunlar.filter((k) => k.summa !== undefined);
  const jami = yozilgan.reduce((s, k) => s + k.summa, 0);
  return {
    kunlar: tushum.length,
    yozilgan: yozilgan.length,
    yozilmagan: kunlar
      .filter((k) => k.summa === undefined)
      .map((k) => k.kun),
    ortacha: yozilgan.length === 0 ? 0 : jami / yozilgan.length,
  };
}

const tushum = [];
tushum[0] = 1200000;
tushum[1] = 1500000;
tushum[3] = 1800000;

console.log(hisobot(tushum));
console.log(hisobot([]));

Konsolda:

text
{ kunlar: 4, yozilgan: 3, yozilmagan: [ 3 ], ortacha: 1500000 }
{ kunlar: 0, yozilgan: 0, yozilmagan: [], ortacha: 0 }

Birinchi qatordagi Array.from — kalit qaror. U teshikni o'tkazmaydi, undefined deb beradi. Shuning uchun har kun obyektga aylanadi va yozilmagan kunni keyin filter aniq topadi. Agar boshida tushum.map(...) yozilsa, teshik obyektga aylanmasdi va "3-kun yozilmagan" degan ma'lumot yo'qolardi. Bo'sh massivda 0 / 0 — NaN bo'lmasligi uchun alohida tekshiruv bor.

Ishga tushiring va commit qiling:

bash
cd ~/kurs/mashqlar
node 10/10-teshikli-massivlar/tushum.js
git add 10/10-teshikli-massivlar
git commit -m "10/10: teshikka chidamli tushum hisobotini qo'sh"

9. Real ishda

  • DOM bilan ishlash. Array.from(document.querySelectorAll(...)) — brauzer kodidagi eng ko'p uchraydigan qatorlardan biri. Elementlarni saralash, filtrlash yoki ma'lumotga aylantirish kerak bo'lsa — avval massivga aylantiriladi.
  • Eski kutubxonalar. jQuery kabi eski kutubxonalar o'z to'plamlarini massivga o'xshash obyekt sifatida qaytarardi ($("li")[0], .length). Eski loyihada ishlasangiz, Array.prototype.slice.call(...) va arguments ni albatta uchratasiz.
  • Ma'lumot tozalash. Jadval (Excel, CSV) dan kelgan ma'lumotda bo'sh kataklar ko'p. Ular JS'ga teshik yoki undefined bo'lib tushadi. Hisobotdan oldin ularni aniq qiymatga aylantirish — tahlilchining birinchi ishi.
  • Intervyu. "[1, , 3].map(x => x * 2) nima qaytaradi?", "arguments massivmi?", "NodeList va massiv farqi?" — junior va middle intervyularida uchraydi. Javoblar: [ 2, <1 empty item>, 6 ]; yo'q — massivga o'xshash; NodeList da map/filter yo'q.

Xulosa

  • Massivga o'xshash obyekt — length va raqamli kalitlar bor, metodlar yo'q: arguments, HTMLCollection, NodeList (unda faqat forEach bor).
  • Array.isArray(x) — haqiqiy massivni aniqlashning ishonchli yo'li.
  • Array.from(x) massivga o'xshashni ham, iterable'ni ham massivga aylantiradi; [...x] — faqat iterable'ni.
  • Teshik — qiymati umuman yo'q katak; in uni undefined dan ajratadi.
  • Eski metodlar (forEach, map, filter, reduce) teshikni o'tkazib yuboradi, yangilari (for...of, Array.from, includes) — undefined deb ko'radi.
  • Teshik yaratmang: delete arr[i], new Array(n), uzoq indeksga yozish o'rniga — splice, Array.from({ length: n }), fill.

Keyingi dars: Typed arrays va binar ma'lumot — fayl, rasm va tarmoq baytlari bilan ishlash uchun ArrayBuffer va Uint8Array kabi maxsus massivlarni o'rganamiz.

Manbalar

  • MDN: "Indexed collections" — "Array-like objects" va "Sparse arrays" bo'limlari; "Array.from()" — developer.mozilla.org
  • V8 blog: "Elements kinds in V8" — v8.dev/blog/elements-kinds
  • ECMAScript 2025 Language Specification: "Array.from", "CreateListFromArrayLike" — tc39.es/ecma262
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript massivga o'xshash obyektlar va teshikli massivlar: arguments, NodeList va bo'sh kataklar — IlmHamroh