2.4-bob: Execution context, hoisting, scope chain va closure (chuqur)
2-QISM — JavaScript (0 dan chuqurgacha) · 4-mavzu
1. Kirish va motivatsiya
Oldingi boblarda o'zgaruvchi 2.1-bob, shart/tsikl 2.2-bob va funksiya 2.3-bob — JS'ning **"nima"**sini ko'rdik. Bu bob esa "nega" va "qanday" ga javob beradi: JS kod aslida qanday ishga tushadi? Nega o'zgaruvchini e'londan oldin ishlatsa ba'zan undefined, ba'zan xato? Closure nima va nega u JS'ning eng kuchli (va eng ko'p so'raladigan) tushunchasi?
Bu bob — JS'ni havaskorona ishlatishdan chinakam tushunishga o'tish nuqtasi. Mana shu tushunchalar:
- Execution context — kod ishlaydigan "muhit" (0.6: abstraction).
- Hoisting — nega
varvafunction"yuqoriga ko'tariladi". - TDZ — nega
let/conste'londan oldin xato beradi. - Scope chain — JS o'zgaruvchini qanday "qidiradi" (2.3 scope ning ichki mexanizmi).
- Closure — funksiyaning o'z "tug'ilgan muhitini eslab qolishi" — React hook'lar, modullar, event handler'larning asosi.
Diqqat: bu — intervyularning eng sevimli mavzusi. "Closure nima?" — frontend intervyularda deyarli doim so'raladi. Bu bobni o'zlashtirsangiz, JS sizga "sehr" emas, mexanizm bo'lib ko'rinadi.
2. Nazariya — chuqur tushuntirish
2.1. Execution context (bajarilish konteksti)
Execution context — JS kodi ishlaydigan muhit: u o'zgaruvchilar, funksiyalar, scope chain va this haqidagi ma'lumotni saqlaydi. Ikki asosiy tur:
- Global Execution Context — dastur boshlanganda yaratiladi (butun fayl uchun bitta).
- Function Execution Context — har funksiya chaqirilganda yangi yaratiladi.
Har bir context ikki bosqichda ishlaydi:
1. CREATION (yaratish) bosqichi:
- o'zgaruvchi va funksiyalar uchun XOTIRA ajratiladi (hoisting shu yerda!)
- var undefined bilan; let/const "init qilinmagan" (TDZ); function to'liq
- scope chain va `this` o'rnatiladi
2. EXECUTION (bajarish) bosqichi:
- kod QATORMA-QATOR ishlaydi
- o'zgaruvchilarga haqiqiy qiymat tayinlanadiMana shu ikki bosqich — hoisting va TDZ'ning sababi.
2.2. Call Stack — kontekstlar to'plami
Kontekstlar call stack'da (0.1: stack) saqlanadi — "oxirgi kirgan, birinchi chiqadi" (LIFO):
function a() { b(); }
function b() { c(); }
function c() { console.log("salom"); }
a();
Call Stack o'zgarishi:
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ │ │ c │ │ │ │ │
│ b │ │ b │ │ b │ │ │
│ a │ │ a │ │ a │ │ a │ (bo'shaydi)
│ glob│ │ glob│ │ glob│ │ glob│
└─────┘ └─────┘ └─────┘ └─────┘
a() chaq. c gacha c tugadi hammasi tugadi(0.1: Maximum call stack size exceeded — stack to'lib ketishi; cheksiz rekursiya.)
2.3. Hoisting (ko'tarilish)
Hoisting — JS'ning creation bosqichida 2.1-bob e'lonlarni scope "tepasiga ko'tarilgandek" ko'rsatishi. Lekin muhim: faqat e'lon ko'tariladi, tayinlash emas:
console.log(x); // undefined (xato emas!) — var hoisted, undefined bilan
var x = 5;
console.log(x); // 5
// Aslida JS buni shunday "ko'radi":
// var x; e'lon yuqoriga (creation: undefined)
// console.log(x); undefined
// x = 5; tayinlash o'z joyida (execution)Function declaration — to'liq hoisted (e'londan oldin chaqirsa bo'ladi):
salom(); // "Salom!" — function declaration to'liq ko'tariladi
function salom() { return console.log("Salom!"); }Lekin function expression va arrow — o'zgaruvchi qoidasiga bo'ysunadi:
salom(); // xato — const hoisted, lekin init qilinmagan (TDZ — 2.4)
const salom = () => console.log("Salom!");2.4. TDZ (Temporal Dead Zone) — let/const farqi
var hoisted bo'lib undefined oladi. let/const esa hoisted bo'ladi, lekin init qilinmaydi — ular e'lon qilingan qatorgacha **TDZ (vaqtinchalik o'lik zona)**da turadi va ularga murojaat ReferenceError beradi:
console.log(a); // undefined (var — hoisted + undefined)
console.log(b); // ReferenceError (let — TDZ)
var a = 1;
let b = 2; ┌──────────── TDZ (b uchun) ─────────────┐
console.log(b); bu yerda b'ga murojaat = ReferenceError
let b = 2; TDZ shu yerda tugaydi (b init bo'ldi)
console.log(b); endi xavfsiz (2)Nega
let/constafzal 2.1-bob: TDZ — bu himoya. U sizni o'zgaruvchini e'londan oldin xato ishlatishdan ogohlantiradi (varesa jimginaundefinedberib, yashirin bug yaratadi). Shuning uchunvarni ishlatmang.
2.5. Scope chain (scope zanjiri) — qidirish mexanizmi
Zanjirni tushunishdan oldin — scope (qamrov) nima va qanday turlari bor. Scope — o'zgaruvchi ko'rinadigan (murojaat qilsa bo'ladigan) hudud. JS'da uch tur:
- Global scope — fayl (yoki modul) eng tashqarisi. Bu yerdagi o'zgaruvchi hamma joydan ko'rinadi.
- Function scope (funksiya qamrovi) — funksiya ichi. Funksiya ichida e'lon qilingan o'zgaruvchi (shu jumladan
var) faqat shu funksiya ichida yashaydi. - Block scope (blok qamrovi) —
{ }ichi (if,for, oddiy blok).let/constblock-scope: ular faqat e'lon qilingan blok ichida ko'rinadi.varesa block-scope'ni e'tiborsiz qoldiradi — u faqat function-scope 2.8-bob.
let global = "G"; // global scope
function f() {
let funk = "F"; // function scope (faqat f ichida)
if (true) {
let blok = "B"; // block scope (faqat shu if ichida)
var eskicha = "V"; // var — blokni "yorib o'tadi", f ichida qoladi
}
console.log(funk, eskicha); // "F V" — var blokdan chiqib ketdi
// console.log(blok); // ReferenceError — blok scope tashqarida yo'q
}Bu uch scope uyalangan (nested): block function ichida, function global ichida. Mana shu uyalanish keyingi qidiruv mexanizmini — scope chain'ni — keltirib chiqaradi.
Har execution context'ning lexical environmenti bor — o'zgaruvchi-qiymat jadvali + tashqi muhitga havola. Bu havolalar zanjiri — scope chain.
JS o'zgaruvchini qidirganda: ichkidan tashqiga qarab yuradi — topilguncha yoki global'gacha:
const global = "global";
function tashqi() {
const tashqiVar = "tashqi";
function ichki() {
const ichkiVar = "ichki";
console.log(ichkiVar); // o'z scope'ida topadi
console.log(tashqiVar); // yo'q ota scope'da topadi (tashqi)
console.log(global); // yo'q ota ota (global)da topadi
}
ichki();
}
tashqi(); Scope chain (qidirish yo'nalishi):
ichki scope tashqi scope global scope
(ichkiVar) (tashqiVar) (global)
│ │ │
└── topilmasa, tashqiga qarab yuradi ──┘Muhim: qidiruv faqat ichkidan tashqiga (bola ota). Tashqi scope ichki scope o'zgaruvchisini ko'ra olmaydi 2.3-bob. Bu — leksik (lexical) scope: scope kod qayerda yozilgani bilan belgilanadi (qayerdan chaqirilgani bilan emas).
2.6. Closure (yopilma) — eng muhim tushuncha
Closure — funksiyaning o'zi + uning tug'ilgan leksik muhiti (scope chain) birga. Oddiyroq: ichki funksiya tashqi funksiyaning o'zgaruvchilarini "eslab qoladi" — hatto tashqi funksiya tugagandan keyin ham.
function hisoblagich() {
let son = 0; // bu o'zgaruvchi "yopib qolinadi"
return function () { // ichki funksiya son'ni "eslaydi"
son++;
return son;
};
}
const oshir = hisoblagich(); // hisoblagich() tugadi, LEKIN son yashab qoladi!
console.log(oshir()); // 1
console.log(oshir()); // 2
console.log(oshir()); // 3 son saqlanib, oshib bormoqda (closure!)Nima sodir bo'ldi? hisoblagich() tugadi, lekin u qaytargan ichki funksiya songa havolani ushlab turibdi. Shuning uchun son xotiradan o'chmaydi (0.1: garbage collection uni saqlaydi) va har chaqiriqda saqlanadi.
O'xshatish: closure — bu funksiyaning "orqa cho'ntagi". Funksiya tug'ilganda, atrofidagi o'zgaruvchilarni cho'ntagiga solib oladi va qayerga borsa ham o'zi bilan olib yuradi. Tashqi funksiya "uy" buzilsa ham, cho'ntakdagi narsa qoladi.
2.7. Closure nega muhim? (amaliy foydalar)
Closure — JS'ning butun ekotizimida ishlaydi:
- Private (xususiy) ma'lumot —
songa faqat ichki funksiya orqali murojaat (tashqaridan ko'rinmaydi — inkapsulyatsiya):
function bank(boshlangich) {
let balans = boshlangich; // XUSUSIY — tashqaridan ko'rinmaydi
return {
qoy: (x) => balans += x,
yech: (x) => balans -= x,
korish: () => balans,
};
}
const hisob = bank(100);
hisob.qoy(50);
console.log(hisob.korish()); // 150
// console.log(balans); // ko'rinmaydi — himoyalangan (closure!)- Funksiya fabrikasi (2.3, Misol 4) — sozlangan funksiyalar yaratish.
- React hooks (
useState) — closure ustiga qurilgan 11.5-bob! - Event handler, callback, debounce/throttle — closure'ga tayanadi.
2.8. Mashhur closure tuzog'i — tsikl va var
Klassik intervyu savoli — var + tsikl + closure:
// var bilan — hammasi 3 chiqaradi (!)
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// natija: 3, 3, 3 var function-scope, hammasi BITTA i'ni ulashadi
// let bilan — 0, 1, 2
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// natija: 0, 1, 2 let block-scope, har iteratsiyada YANGI iSababi: var function-scope (bitta i hamma uchun); tsikl tugagach i=3, keyin callback'lar ishlaydi. let esa har iteratsiyada yangi binding yaratadi — closure har biri o'z isini eslaydi. Bu — let/const ning yana bir afzalligi 2.1-bob.
3. Asosiy tushunchalar — tez ma'lumotnoma
Execution Context — kod ishlaydigan muhit (creation + execution bosqich)
Call Stack — kontekstlar to'plami (LIFO, 0.1)
Hoisting — e'lonlar "tepaga"; varundefined, functionto'liq
TDZ — let/const e'londan oldin ReferenceError
Scope chain — o'zgaruvchi qidiruvi: ichkidan tashqiga
Lexical scope — scope kod QAYERDA yozilganida belgilanadi
Closure — funksiya + tug'ilgan muhiti (o'zgaruvchini "eslaydi")4. Batafsil kod namunalari
Misol 1 — Hoisting farqlari (2.3, 2.4)
// var — hoisted, undefined
console.log(a); // undefined
var a = 1;
// let — TDZ xato
try { console.log(b); } catch (e) { console.log(e.name); } // "ReferenceError"
let b = 2;
// function declaration — to'liq hoisted
salom(); // ishlaydi
function salom() { console.log("Salom!"); }
// arrow/expression — TDZ
try { hayr(); } catch (e) { console.log(e.name); } // "ReferenceError"
const hayr = () => console.log("Hayr!");Misol 2 — Scope chain (2.5)
const daraja = "global";
function tashqi() {
const daraja2 = "tashqi";
function ichki() {
const daraja3 = "ichki";
// ichki tashqi global (qidiruv yo'nalishi)
console.log(daraja3, daraja2, daraja); // "ichki tashqi global"
}
ichki();
}
tashqi();Misol 3 — Closure: hisoblagich va private holat (2.6, 2.7)
function yaratHisoblagich() {
let son = 0; // private (closure ichida)
return {
oshir: () => ++son,
kamaytir: () => --son,
qiymat: () => son,
};
}
const c = yaratHisoblagich();
c.oshir();
c.oshir();
c.kamaytir();
console.log(c.qiymat()); // 1
// son tashqaridan ko'rinmaydi — faqat metodlar orqali (inkapsulyatsiya)Misol 4 — Closure tuzog'i va yechimi (2.8)
// var — 3, 3, 3
console.log("var bilan:");
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 3, 3, 3
}
// let — 0, 1, 2
setTimeout(() => {
console.log("let bilan:");
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0); // 0, 1, 2
}
}, 50);5. To'g'ri va noto'g'ri holatlar
1) var ishlatish (hoisting/scope muammosi)
// var — hoisting bilan undefined, function-scope (2.3, 2.8)
function f() { if (true) { var x = 5; } console.log(x); } // 5 (blokdan "chiqib ketdi")
// let/const — block-scope, TDZ himoyasi
function f() { if (true) { let x = 5; } /* x bu yerda yo'q */ }2) O'zgaruvchini e'londan oldin ishlatish
// TDZ ReferenceError (2.4)
console.log(soni);
let soni = 5;
// avval e'lon, keyin ishlat
let soni = 5;
console.log(soni);3) Tsiklda var + closure
// hammasi oxirgi qiymatni ko'radi (2.8)
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i));
// let — har iteratsiya o'z scope'i
for (let i = 0; i < 3; i++) setTimeout(() => console.log(i));4) Closure'ni tushunmay xotira oqishi (memory leak)
// keraksiz katta ma'lumotni closure'da ushlab turish
function f() { const katta = yangiKattaMassiv(); return () => katta[0]; }
// katta butunlay xotirada qoladi (0.1: RAM)
// faqat kerakini ushla
function f() { const birinchi = yangiKattaMassiv()[0]; return () => birinchi; }6. Keng tarqalgan xatolar va yechimlari
Xato 1 — ReferenceError: Cannot access 'x' before initialization
Sababi: let/constni TDZ'da (e'londan oldin) ishlatdingiz 2.4-bob. Yechimi: e'lonni ishlatishdan oldin qo'ying.
Xato 2 — var o'zgaruvchi kutilmagan qiymat (undefined)
Sababi: hoisting — e'lon ko'tarildi, tayinlash yo'q 2.3-bob. Yechimi: varni let/constga almashtiring.
Xato 3 — Tsiklda barcha callback bir xil qiymat
Sababi: var + closure 2.8-bob. Yechimi: let ishlating (har iteratsiyada yangi binding).
Xato 4 — Maximum call stack size exceeded
Sababi: cheksiz rekursiya — call stack to'ldi (2.2, 0.1). Yechimi: rekursiyaga to'xtash sharti (base case — 3.11-bob).
Xato 5 — Closure'da eski qiymat "qotib qolgan"
Sababi: closure o'zgaruvchiga havolani ushlaydi — kutilgan vaqtda o'zgargan/o'zgarmagan bo'lishi mumkin. Yechimi: mexanizmni tushuning 2.6-bob; React'da useEffect dependency 11.5-bob.
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
let/const2.1-bob: TDZ va block-scope — negavaremas.- Funksiyalar 2.3-bob: scope, HOF — bu bobning poydevori.
- Asinxron 2.11-bob: call stack + event loop 5.1-bob — closure callback'larda.
- Modullar 2.14-bob: modul pattern — closure ustiga qurilgan.
- React hooks 11.5-bob:
useState/useEffect— closure mexanizmi; "stale closure" muammosi. - Funksional dasturlash 2.15-bob: closure, HOF.
- Performance/memory (0.1, 11.11): closure va xotira oqishi.
8. Eng yaxshi amaliyotlar (best practices)
varni umuman ishlatmang —const/let(TDZ himoyasi, block-scope — 2.4, 2.8).- O'zgaruvchini ishlatishdan oldin e'lon qiling (TDZ'dan qoching).
- Tsiklda doim
let— closure tuzog'idan saqlaydi 2.8-bob. - Closure'ni private ma'lumot uchun ishlating (inkapsulyatsiya — 2.7).
- Closure'da keraksiz katta ma'lumot ushlamang — xotira oqishi (5-bo'lim, 0.1).
- Scope'ni iloji boricha kichik tuting — o'zgaruvchini kerakli blokda e'lon qiling.
- Closure mexanizmini tushuning — React'da "stale closure" bug'larini oldini oladi 11.5-bob.
- Leksik scope'ni eslang — funksiya qayerda yozilgani muhim, qayerdan chaqirilgani emas.
9. Amaliy loyiha: "Closure bilan Modullar va Holat Boshqaruvi"
Closure'ning amaliy kuchini ko'rsatuvchi loyiha — kutubxonasiz "holat boshqaruvi".
Maqsad
Execution context, scope chain va closure'ni amalda his qilib, private holat va modul pattern yaratish (React state'ning soddalashtirilgan g'oyasi).
Talablar (requirements)
- Hisoblagich moduli:
yaratHisoblagich(boshlangich)—oshir,kamaytir,qiymat,resetmetodli obyekt qaytarsin;sonprivate (closure — Misol 3). - Bank moduli:
bank(boshlangichBalans)—qoy,yech,balansmetodlari; manfiy balansga yo'l qo'ymang (yechda tekshiring);balansprivate 2.7-bob. - Funksiya fabrikasi:
kopaytuvchiYarat(n)—nga ko'paytiruvchi funksiya qaytaradi (closure — 2.6).ikki,uchyarating. - Hoisting tajribasi:
var,let, function declaration va arrow'ni e'londan oldin ishlatib, har birining natijasini (undefined / ReferenceError / ishlaydi) izohlang (2.3, 2.4). - Closure tuzog'i:
varvaletbilan tsikl +setTimeoutfarqini ko'rsating va nega ekanini izohlang 2.8-bob. - ID generator: har chaqiriqda oshib boruvchi noyob ID qaytaruvchi closure (
yaratIdGenerator). - (Bonus) Oddiy "store":
yaratStore(boshlangich)—olish/ozgartirish/obuna(subscribe) bilan (Redux g'oyasi — 12.2).
Maslahatlar (hint)
- Private o'zgaruvchi — tashqi funksiya ichida
let; faqat qaytarilgan metodlar orqali murojaat 2.7-bob. - Bank
yechda:if (summa > balans) return "Mablag' yetarli emas". - ID generator:
let id = 0; return () => ++id;. - Hoisting tajribasida
try/catchbilan xatoni ushlab,e.nameni chiqaring (Misol 1). - Closure tuzog'ida ikki tsikl yozing (
varvalet) — natijani solishtiring (Misol 4).
"Tayyor" mezonlari (acceptance criteria)
- Hisoblagich va bank modullari ishlaydi; ichki holat tashqaridan ko'rinmaydi.
- Bank manfiy balansga yo'l qo'ymaydi.
- Funksiya fabrikasi (closure) to'g'ri ishlaydi.
- Hoisting tajribasi 4 holatni (var/let/function/arrow) ko'rsatib, izohlangan.
- Closure tuzog'i (var vs let) ko'rsatilgan va izohlangan.
- ID generator noyob, oshib boruvchi ID beradi.
- Kod izohlarida "nega" tushuntirilgan.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda JS'ning ichki mexanizmini chuqur ochdik:
- Execution context — kod ishlaydigan muhit; creation (hoisting shu yerda) + execution bosqichlari. Call stack — kontekstlar to'plami (LIFO).
- Hoisting: e'lonlar "tepaga";
varundefined, function declaration to'liq,let/constTDZ (ReferenceError). - Scope chain: o'zgaruvchi qidiruvi ichkidan tashqiga; leksik scope — kod qayerda yozilgani muhim.
- Closure: funksiya + tug'ilgan muhiti; o'zgaruvchini eslaydi (tashqi funksiya tugagach ham). Private holat, funksiya fabrikasi, React hooks — hammasi shunga tayanadi.
- Closure tuzog'i: tsiklda
var(bitta binding) vslet(har iteratsiyada yangi).
Keyingi bob — 2.5-bob: this, prototype, prototypal inheritance (chuqur). Scope va closure'ni tushundik; endi JS'ning yana ikki "sirli" tushunchasini — this (kontekstga qarab o'zgaradi) va prototype (JS'ning meros mexanizmi) ni chuqur ochamiz. Bular OOP 2.10-bob va butun JS obyekt tizimining asosi.
Foydalanilgan rasmiy/ishonchli manbalar
- MDN Web Docs — Closures, scope, lexical environment
- MDN Web Docs — Hoisting, Temporal Dead Zone,
let/const/var - MDN Web Docs — execution context, call stack
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!