Series: JavaScript Basics Lesson 22

Object ichki tuzilishi

V8 obyektlarni qanday saqlaydi — Hidden Class, offset, Transition Chain, Inline Cache va Dictionary Mode ga o'tish.

22-dars

Object ichki tuzilishi

1. Nega bu mavzu muhim?

{} yozganingizda "oddiy obyekt yaratdim" deb o'ylaysiz. Lekin V8 buni hash-map sifatida saqlamaydi (garchi JS spetsifikatsiyasida object "key-value collection" deb ta'riflansa ham). Agar shunday bo'lganida, har bir obj.name chaqiruvi hash hisoblab, keyin qidiruv qilardi — bu millionlab marta chaqirilganda juda sekin bo'lardi.

V8 buning o'rniga Hidden Class (ichki nomi — Map, boshqa enginelarda "Shape" yoki "Structure") degan mexanizm orqali obyektlarni statik tildagi struct kabi ishlaydi.


2. Obyekt ikki qismga bo'linadi

const user = { name: 'Ali', age: 25 };

V8 buni shunday saqlaydi:

A) Hidden Class (metadata) — qanday property lar bor va ular qaysi offsetda:

HiddenClass_C2: { name → offset 0, age → offset 1 }

B) Property Storage (real qiymatlar) — massiv kabi ketma-ket xotira:

user:
  properties[0] = 'Ali'
  properties[1] = 25
  hiddenClassPointer → C2

user.name chaqirilganda V8 hash hisoblamaydi — Hidden Class dan name ning offseti 0 ekanini bilib, to'g'ridan-to'g'ri properties[0] ga boradi. Bu O(1), pointer arifmetikasi darajasida tez — massiv indexiga murojaat qilish kabi.

Offset degani nima

Tasavvur qiling:

const user = {
  name: "Ali",
  age: 20,
  city: "Toshkent"
}

Hidden Class shunday deydi:

  • name → offset 0
  • age → offset 1
  • city → offset 2

Shuning uchun user.age qidirib o'tirmaydi — birdaniga age = offset 1 deb oladi. Offset — property qaysi indexda joylashganini bildiruvchi raqam, xolos.


3. Transition Chain — Hidden Class qanday quriladi

const obj = {};        // C0: {}
obj.x = 1;              // C0 → C1: {x}
obj.y = 2;              // C1 → C2: {x, y}

Har bir yangi property qo'shilganda V8 yangi Hidden Class yasaydi va oldingisidan transition qiladi:

        add "x"          add "y"
C0 ───────────→ C1 ───────────→ C2
{}              {x}             {x, y}

Bu daraxt shaklida — bitta Hidden Class dan bir nechta yo'nalishda transition bo'lishi mumkin:

              C1 {x}
             /       \
      add "y"         add "z"
        C2 {x,y}      C3 {x,z}

Muhim natija: agar ikkita obyekt bir xil tartibda bir xil property qo'shsa, ular bitta Hidden Classni bo'lishadi:

function makePoint(x, y) {
  const p = {};
  p.x = x;
  p.y = y;
  return p;
}

const p1 = makePoint(1, 2); // C2 {x, y}
const p2 = makePoint(3, 4); // xuddi shu C2 {x, y}

Bu V8 xotirasini tejaydi (Hidden Class faqat bir marta yaratiladi) va Inline Cache (IC) optimallashtirishga imkon beradi — chunki JIT compiler "bu funksiya doim C2 shaklidagi obyektlar bilan ishlaydi" deb bilib, tezkor mashina kodi generatsiya qiladi.


4. Constructor orqali — eng barqaror yo'l

function Point(x, y) {
  this.x = x;
  this.y = y;
}

const p1 = new Point(1, 2);
const p2 = new Point(3, 4);

Bu yerda this.x va this.y har doim bir xil tartibda qo'shiladi (constructor kodi bir xil bo'lgani uchun) — shu sababli barcha Point instance lari bitta Hidden Classga ega bo'ladi. Bu classlarning (va constructor function larning) tezligi sirlaridan biri.

Solishtiring — tartib buzilsa:

function A() { this.x = 1; this.y = 2; }
function B() { this.y = 1; this.x = 2; } // teskari tartib!

const a = new A(); // Hidden Class: {x, y}
const b = new B(); // Hidden Class: {y, x} — BOSHQA!

Property nomlari bir xil, lekin qo'shilish tartibi farqli — shuning uchun a va b turli Hidden Classlarga ega. Agar bitta funksiya ba'zan A tipidagi, ba'zan B tipidagi obyektlar bilan ishlasa, bu funksiya polymorphic (yoki megamorphic) bo'lib qoladi — V8 Inline Cache buzilib, sekinlashadi.


5. Inline Cache (IC) — bu nima va nega muhim?

function getX(obj) {
  return obj.x;
}

Bu funksiya birinchi marta chaqirilganda V8 "sekin yo'l" bilan obj.x ni topadi va shu obyektning Hidden Classini eslab qoladi:

IC state: "Monomorphic" — faqat C2 shaklidagi obyektlar kutilmoqda, offset 0

Keyingi chaqiruvlarda, agar kelgan obyekt xuddi shu Hidden Class (C2) bo'lsa, V8 hech qanday qidiruv qilmasdan, to'g'ridan-to'g'ri offset 0 dan o'qiydi — bu deyarli C++ struct field access tezligida.

Agar funksiyaga har xil shakldagi obyektlar kelsa:

getX({ x: 1 });          // C_a
getX({ x: 1, y: 2 });    // C_b — boshqa shape
getX({ y: 1, x: 2 });    // C_c — yana boshqa

IC holati bosqichma-bosqich yomonlashadi:

Monomorphic (1 shape) → Polymorphic (2-4 shape) → Megamorphic (4+ shape)

Megamorphic bo'lganda V8 optimallashtirishdan butunlay voz kechadi va sekin, umumiy qidiruv yo'liga qaytadi. Shuning uchun — bir xil funksiyaga doim bir xil "shakldagi" obyektlar berish (consistent object shape) performance uchun juda muhim.


6. Dictionary Mode (Slow Mode) — Hidden Class dan voz kechish

Ba'zi holatlarda V8 Hidden Class mexanizmidan butunlay voz kechadi va obyektni haqiqiy hash-map (dictionary) rejimiga o'tkazadi:

const obj = { a: 1, b: 2, c: 3 };
delete obj.b; // ⚠️ Dictionary mode ga o'tish trigger bo'lishi mumkin

Nega delete xavfli

Transition Chain faqat qo'shishga mo'ljallangan — daraxt strukturasi. Agar o'rtadan property o'chirilsa, V8 uchun yangi, mos Hidden Class qidirish murakkablashadi.

Tasavvur qiling:

const obj = {};
obj.a = 1;
obj.b = 2;
obj.c = 3;
// Hidden Class: { a, b, c }

delete obj.b;
// Endi property lar: { a, c }
// Bu optimizatsiyani buzadi

Shu sababli ko'p property o'chirilsa yoki juda ko'p (minglab) dinamik property qo'shilsa, V8 obyektni Dictionary Mode ga o'tkazadi — "endi Hidden Class bilan ovora bo'lmayman" deb, property lar endi offsetlar orqali emas, haqiqiy hash-table orqali saqlanadi.

Dictionary mode:

  • ✅ Moslashuvchan — istalgan tartibda qo'shish/o'chirish oson
  • ❌ Sekinroq — har bir access hash hisoblashni talab qiladi
  • ❌ Inline Cache ishlamaydi

Amaliy maslahat: agar obyektingiz "struct" kabi (fixed shape, masalan {x, y, z}) bo'lsa — Hidden Class rejimida qoldiring. Agar chindan ham dinamik key-value store kerak bo'lsa (masalan runtime da noma'lum keylar) — Map obyektidan foydalaning, oddiy {} emas, chunki Map aynan shu maqsad uchun optimallashtirilgan.


7. Hash Table — Dictionary Mode qanday qidiradi

Hash Table degani oddiy lug'at. Masalan obj.age:

age
  ↓
hash()
  ↓
123456
  ↓
20

Offset ishlatmaydi — hash orqali topadi. Shuning uchun biroz sekinroq.


8. Xotira tuzilishi — vizual umumlashtirish

┌─────────────────────┐
│   user (object)      │
│  ┌────────────────┐  │
│  │ HiddenClass ptr │──┼──→ HiddenClass_C2 { name:0, age:1 }
│  ├────────────────┤  │
│  │ properties[0]   │──┼──→ 'Ali'
│  ├────────────────┤  │
│  │ properties[1]   │──┼──→ 25
│  ├────────────────┤  │
│  │ __proto__ ptr   │──┼──→ Object.prototype
│  └────────────────┘  │
└─────────────────────┘

Hammasi bitta rasmda:

Object yaratildi
       ↓
      C0
       ↓
  x qo'shildi
       ↓
      C1
       ↓
  y qo'shildi
       ↓
      C2
       ↓
   delete y
       ↓
Dictionary Mode
       ↓
   Hash Table