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.