Seriya: JavaScript Basics Dars 33

Map

Nega Map kerak, istalgan turdagi key, V8 ichida hash table + insertion order, Map vs Object performance va to'liq metodlar solishtiruvi.

33-dars

Map

1. Nega Map kerak — oddiy object yetarli emasmi?

Oddiy {} Hidden Class + Transition Chain orqali ishlaydi, va bu fixed-shape struct kabi optimallashtirilgan. Lekin haqiqiy, dinamik key-value store kerak bo'lganda (masalan runtime'da noma'lum, tez-tez qo'shiladigan/o'chiriladigan key'lar), oddiy object muammoli:

const obj = {};
obj['user1'] = 'Ali';
obj['user2'] = 'Vali';
delete obj['user1']; // ESLAYSIZMI — bu Dictionary Mode'ga MAJBURLAYDI!

delete — Transition Chain'ni buzadi, obyektni **slow mode (dictionary)**ga o'tkazadi. Bundan tashqari, oddiy object'da faqat string yoki Symbol key bo'lishi mumkin:

const obj = {};
const keyObj = { id: 1 };
obj[keyObj] = 'qiymat'; // keyObj AVTOMATIK STRING'GA AYLANADI: '[object Object]'!

const keyObj2 = { id: 2 };
console.log(obj[keyObj2]); // 'qiymat' — XATO! chunki ikkalasi ham '[object Object]'ga aylangan

Map — aynan shu muammolarni hal qilish uchun yaratilgan: haqiqiy, optimallashtirilgan key-value store, istalgan turdagi key bilan.


2. Map yaratish va asosiy metodlar

const map = new Map();

map.set('name', 'Ali');
map.set(42, 'raqam key');
map.set(true, 'boolean key');

const objKey = { id: 1 };
map.set(objKey, 'obyekt key!'); // OBYEKTNING O'ZI key sifatida, string'ga aylanmaydi!

console.log(map.get('name'));  // 'Ali'
console.log(map.get(objKey));  // 'obyekt key!' — TO'G'RI ISHLAYDI!
console.log(map.size);         // 4

Muhim farq: map.get(objKey) ishlashi uchun aynan shu pointer kerak:

const anotherObj = { id: 1 }; // MAZMUNI bir xil, lekin BOSHQA manzil
console.log(map.get(anotherObj)); // undefined! — chunki bu boshqa pointer

Bu — Primitiv vs Non-primitiv qoidasining davomi: Map ichida key solishtirish SameValueZero algoritmi orqali ishlaydi (deyarli === kabi, faqat NaN === NaN bu yerda true hisoblanadi — yagona farq).


3. V8 ichida Map qanday saqlanadi

Bu — eng muhim texnik qism. Map — Hidden Class + Transition Chain mexanizmidan butunlay boshqa tuzilishga ega. V8 ichida Map — hash table asosida ishlaydi (order-preserving hash table, ya'ni qo'shilish tartibi saqlanadi):

Map ICHKI TUZILISHI (soddalashtirilgan):

┌─────────────────────────────────┐
│ HashTable                        │
│  bucket[0] → [entry: 'name'→'Ali']
│  bucket[1] → [entry: 42→'raqam key']
│  bucket[2] → [entry: objKey→'obyekt key!']
│  ...
├─────────────────────────────────┤
│ Insertion Order Linked List      │  ← Map o'z tabiatiga ko'ra TARTIBNI SAQLAYDI!
│  'name' → 42 → true → objKey     │
└─────────────────────────────────┘

Muhim: Map — qo'shilish tartibini kafolatlaydi (for...of yoki forEach bilan aylanganda, aynan qo'shilgan tartibda keladi). Bu — oddiy object'dan farq (object'da ham zamonaviy enginelarda tartib odatda saqlanadi, lekin bu spetsifikatsiya darajasida kafolatlangan emas integer bo'lmagan key'lar uchun — Map esa har doim, kafolatlangan tartibda ishlaydi).


4. Performance — nega ba'zan Map tezroq, ba'zan sekinroq

// Ko'p marta qo'shish/o'chirish bo'lsa — Map TEZROQ
const map = new Map();
for (let i = 0; i < 100000; i++) {
  map.set(i, i * 2);
}
map.delete(500); // Map'da delete — HECH QANDAY "degrade" YO'Q!

// Object'da esa delete — Dictionary Mode'ga majburlaydi
const obj = {};
for (let i = 0; i < 100000; i++) {
  obj[i] = i * 2;
}
delete obj[500]; // Hidden Class zanjiri BUZILADI

Nega? Chunki Map — boshidanoq dinamik key-value store bo'lish uchun mo'ljallangan (hash table), delete/add operatsiyalari uning tabiiy ishlash rejimi. Object — fixed-shape struct bo'lish uchun optimallashtirilgan, delete esa uni majburiy ravishda boshqa (sekinroq) rejimga o'tkazadi.

Lekin — agar key'lar oldindan ma'lum, barqaror bo'lsa (masalan {x, y, z} struct kabi), oddiy object tezroq — chunki Hidden Class + Inline Cache orqali offset asosida to'g'ridan-to'g'ri o'qish, hash hisoblashsiz.


5. Map metodlari — to'liq

const map = new Map([['a', 1], ['b', 2]]); // array'dan yaratish

map.has('a');       // true
map.get('a');        // 1
map.set('c', 3);     // qo'shish (yoki mavjudini yangilash)
map.delete('a');     // true — o'chirildi
map.size;            // 2

// Iteratsiya — HAR DOIM qo'shilish tartibida
for (const [key, value] of map) {
  console.log(key, value);
}

map.forEach((value, key) => console.log(key, value));

[...map.keys()];    // ['b', 'c']
[...map.values()];  // [2, 3]
[...map.entries()]; // [['b', 2], ['c', 3]]

map.clear(); // barchasini o'chirish

6. Map vs Object — to'liq solishtirish jadvali

Object Map
Key turi Faqat string/Symbol Istalgan tur (obyekt, funksiya, primitiv)
Tartib Integer key'lar tartiblanadi, qolgani qo'shilish tartibida (kafolatlanmagan edge case'lar bor) Har doim qo'shilish tartibida, kafolatlangan
size olish Object.keys(obj).length — hisoblash kerak map.size — to'g'ridan-to'g'ri property, tez
Iteratsiya for...in (prototype chain'ga ham kiradi!) yoki Object.keys() for...of — to'g'ridan-to'g'ri, faqat o'ziniki
Prototype pollution xavfi Bor (Object.prototypedan meros) Yo'q — Map.prototype metodlar bilan aralashmaydi
Ichki tuzilma Hidden Class + Transition Chain Hash Table + Insertion Order List
delete narxi Qimmat (Dictionary Mode'ga o'tkazadi) Arzon (tabiiy operatsiya)
JSON.stringify Ishlaydi Ishlamaydi ({} bo'lib chiqadi, avval Object.fromEntries kerak)

7. Map ham prototype chain'ga ega — eslatma

const map = new Map();
console.log(map.__proto__ === Map.prototype); // true
console.log(Map.prototype.__proto__ === Object.prototype); // true

Map — bu built-in constructor function (yoki class), va u ham Prototype Chain'da ko'rgan bir xil mexanizmga bo'ysunadi:

map → Map.prototype { get, set, has, delete, ... } → Object.prototype → null

Farqi shundaki — mapning o'zidagi ma'lumotlar (key-value juftliklari) Hidden Class orqali emas, balki ichki hash tableda (JS kod darajasida ko'rinmas, faqat V8 C++ implementatsiyasida) saqlanadi. get/set kabi metodlar esa — oddiy prototype orqali, boshqa obyektlardagi kabi bo'lishiladi.


Xulosa

Map — Hidden Class + Transition Chain o'rniga hash table + insertion-order mexanizmi asosida ishlaydigan, haqiqiy dinamik key-value store: u istalgan turdagi key'ni (shu jumladan obyektlarning o'zini, string'ga aylantirmasdan) qabul qiladi, qo'shilish tartibini kafolatlaydi, va delete/dinamik qo'shish operatsiyalarida oddiy object'dagi Dictionary Mode degradatsiyasidan aziyat chekmaydi — shuning uchun tez-tez o'zgaradigan, key turi noma'lum ma'lumotlar uchun object'dan afzalroq.