Series: JavaScript Basics Lesson 82

Refactoring

Refactoring texnikalarining V8'ga real ta'siri: Extract Function'ning JIT inlining orqali deyarli bepul bo'lishi, Rename'ning V8'ga umuman ta'sir qilmasligi, Parameter Object'ning hidden class tartibiga bog'liqligi, va Replace Conditional with Polymorphism'ning megamorphic deoptimization xavfi.

82-dars

Refactoring

Refactoring texnikalarining aksariyati (Extract Function, Rename, Parameter Object) — bu sof matn/struktura darajasidagi o'zgarish, V8 ularni umuman sezmaydi — final bajariladigan mantiq bir xil qoladi. Lekin ba'zi refactoring turlari V8'ning ichki optimallashtirish qarorlariga bevosita ta'sir qiladi.

1. Extract Function va Function Inlining — JIT bilan bog'liqlik

TurboFan haqida gaplashganimizda, JIT kompilyator kichik, tez-tez chaqiriladigan funksiyalarni ba'zan inline qiladi — ya'ni, funksiya chaqiruvi o'rniga uning tanasini to'g'ridan-to'g'ri chaqirilgan joyga joylashtiradi (call overhead'dan qutulish uchun):

// Refactoring'dan KEYIN - Extract Function qilingan
function calculateTax(price) {
  return price * 0.12;
}

function getTotalPrice(price) {
  return price + calculateTax(price);
}

Ko'pchilik dasturchi noto'g'ri qo'rqadi: "Men funksiyani ajratsam, qo'shimcha funksiya chaqiruvi (call) qo'shiladi, bu sekinlashtiradi". Amalda, agar calculateTax kichik va barqaror (hidden class ma'nosida) bo'lsa, TurboFan uni avtomatik inline qiladi, va natijaviy mashina kodi xuddi qo'lda birlashtirilgan versiyadek tez ishlaydi. Ya'ni: Extract Function refactoring odatda "bepul" — kod o'qilishi yaxshilanadi, ishlash tezligiga deyarli ta'sir qilmaydi, chunki JIT bu farqni orqada, sizdan yashirin tekislab qo'yadi.

Lekin bu har doim ham bo'lavermaydi — agar funksiya juda katta bo'lsa yoki polimorfik (turli xil shakldagi argumentlar bilan chaqirilsa) bo'lsa, TurboFan inline qilishdan voz kechishi mumkin (bu — "inlining budget" deb ataladigan ichki heuristika bilan bog'liq).


2. Rename — bu V8'ga hech qanday ta'sir qilmaydi

Bu — eng "arzon" refactoring, va buni alohida ta'kidlash kerak: let d = 5 ni let discountRate = 5ga o'zgartirish — V8'ning parse/compile/execute jarayoniga mutlaqo hech qanday ta'sir qilmaydi. Nomlar — bu faqat inson uchun mavjud metama'lumot (source code darajasida); V8 ichki bytecode (Ignition) darajasida o'zgaruvchilarni registr yoki stack slot indekslari orqali ishlatadi, nomning o'zi umuman saqlanmaydi ham (production build'da, minifikatsiyadan keyin esa aniq yo'qoladi).


3. "Introduce Parameter Object" — Hidden Class'lar bilan bog'liqlik

Bu yerda kutilmagan, foydali nozik ta'sir bor. Eslang: V8 obyektlarga hidden class biriktiradi, va agar bir xil "shakldagi" obyektlar bir xil tartibda yaratilsa, V8 ularni bitta hidden class'ga bog'lab, tezroq murojaat qila oladi.

// OLDIN - har xil joyda turlicha tartibda chaqiriladi
function createUser(name, age, email) { /* ... */ }

createUser('Aziz', 25, 'a@mail.com');
createUser('Vali', 'v@mail.com', 30); // BOSHQA dasturchi tartibni chalkashtirib yubordi!

// KEYIN - Parameter Object bilan
function createUser({ name, age, email }) { /* ... */ }

createUser({ name: 'Aziz', age: 25, email: 'a@mail.com' });
createUser({ email: 'v@mail.com', name: 'Vali', age: 30 }); // property TARTIBI muhim emas

Diqqat qiling: obyekt literal ichida property tartibi boshqacha bo'lsa ham (email birinchi yozilgan), natijaviy obyekt funksional jihatdan bir xil ishlaydi — lekin V8 hidden class darajasida buni farqli deb belgilashi mumkin, chunki hidden class transition property qo'shilish tartibiga bog'liq. Bu — refactoring to'g'ri qilingan bo'lsa ham, mikro-optimallashtirish nuqtai nazaridan e'tiborga olinishi kerak bo'lgan nozik holat: agar bu funksiya hot path'da (juda tez-tez, millionlab marta chaqiriladigan joyda) bo'lsa, obyektlarni doim bir xil tartibda yaratish tavsiya etiladi, hidden class'lar barqaror qolishi uchun.


4. Replace Conditional with Polymorphism — Deoptimization xavfi

Bu — JIT & Deoptimization mavzusi bilan eng chuqur bog'liq joy. Agar siz uzun if/elseni class metodlariga bo'lib chiqarsangiz, lekin bitta massiv ichida turli class'lardagi obyektlar aralashib yursa:

const shapes = [new Circle(5), new Rectangle(4, 6), new Triangle(3, 4)];
shapes.forEach(shape => console.log(shape.area()));

Bu yerda shape.area() chaqiruvi — V8 uchun polimorfik (bir nechta turli hidden class bilan chaqirilyapti). Agar shapes.forEach hot loop bo'lsa va turlar soni ko'p bo'lsa (megamorphic holatga o'tsa — odatda 4+ turli shape), TurboFan bu chaqiruvni optimallashtirishdan voz kechadi, va har safar sekinroq, umumiy (generic) yo'l orqali area()ni chaqiradi.

Bu — Open/Closed refactoring'ning "narxi": kod arxitektura jihatidan yaxshilanadi (yangi shape qo'shish oson), lekin agar bu millionlab marta, tezlik kritik joyda ishlatilsa, if/else (monomorfik, bitta funksiya ichida) ba'zan tezroq bo'lishi mumkin, chunki V8 uni bitta, barqaror yo'l sifatida optimallashtira oladi. Bu — real amaliyotda kamdan-kam muammo (chunki aksariyat kod hot path emas), lekin performance-critical kodni (masalan, o'yin dvigateli, real-time grafik) refactoring qilganda, bu bilinishi kerak bo'lgan trade-off.


5. Static analysis (ESLint, IDE Rename) — bu ham AST darajasida ishlaydi

Minifikatsiya mavzusida ko'rgan AST (Abstract Syntax Tree) tushunchasi bu yerda ham qaytadan ishga tushadi: IDE'ning "Rename Symbol" funksiyasi kodni regex bilan qidirmaydi (bu xato-qiluvchan bo'lardi — masalan, string ichidagi bir xil so'zni ham almashtirib qo'yishi mumkin edi). Buning o'rniga, IDE kodni parse qilib, AST quradi, so'ngra scope tahlili orqali qaysi user o'zgaruvchisi aynan shu deklaratsiyaga tegishli ekanini aniqlaydi (Lexical Scope tushunchasi — IDE bu yerda xuddi V8'ning o'zi qilgan ishni, faqat boshqa maqsadda, takrorlaydi), va faqat shu aniq referenslarni almashtiradi.


Xulosa — amaliy qoida

Refactoring turi V8'ga ta'siri
Rename Yo'q (mutlaqo bepul)
Extract Function Deyarli yo'q (JIT inline qiladi, agar kichik bo'lsa)
Parameter Object Kichik xavf (hidden class tartibi) — faqat hot path'da muhim
Conditional → Polymorphism Real xavf bo'lishi mumkin — faqat hot loop + ko'p tur holatida

99% holatlarda refactoring V8 performance haqida umuman o'ylamasdan qilinishi kerak — kodning o'qilishi, testlanishi ustunroq. Faqat profiling orqali (DevTools Profiling) haqiqiy hot path aniqlangandan keyin, o'sha aniq joyda, yuqoridagi nozik holatlar hisobga olinadi — bu, aslida, mashhur qoidaning o'zi: "Premature optimization is the root of all evil" (avvalo to'g'ri, toza kod yozing, keyin o'lchab, kerak bo'lgan joyda optimallashtiring).