Mundarija (33)
- Bu darsda
- 1. Nega bu kerak?
- 2. Sirni yaqindan ko'ramiz
- 2.1 Hisoblagich yasovchi funksiya
- 2.2 Tashqaridan tegib bo'lmaydi
- 3. Muhit va "orqa cho'ntak"
- 3.1 Eslab olamiz: har chaqiruv — yangi muhit
- 3.2 Funksiya tug'ilgan joyini eslaydi
- 3.3 Closure'ning ta'rifi
- 4. Har chaqiruv — yangi muhit
- 4.1 Ikki mustaqil hisoblagich
- 4.2 Qaytgan funksiyani saqlamaslik
- 5. Closure o'zgaruvchini eslaydi, nusxasini emas
- 5.1 Jonli bog'lanish
- 5.2 Ikki funksiya — bitta muhit
- 6. Qaysi muhit: yozilgan joy, chaqirilgan joy emas
- 7. DevTools'da closure'ni ko'rish
- 7.1 [[Scopes]]
- 7.2 Hamma narsa eslab qolinmaydi
- 7.3 To'xtash nuqtasida
- 8. Ko'p uchraydigan xatolar
- 8.1 Funksiya o'rniga natijani qaytarish
- 8.2 Yasovchini har safar qayta chaqirish
- 8.3 Tashqi o'zgaruvchini soyalash
- 8.4 "Closure nusxa oladi" deb o'ylash
- 9. Mashqlar
- 1-mashq (oson): Salomlashuvchi
- 2-mashq (o'rta): Stol navbati
- 3-mashq (qiyin): Natijani oldindan ayting
- 4-mashq: Vazifalar qadami — keyingiId ni kim buzishi mumkin?
- 10. Real ishda
- Xulosa
- Manbalar
JavaScript closure: funksiya o'z muhitini eslab qoladi
Qisqacha: Closure — funksiya va u tug'ilgan muhitning birikmasi. Funksiya yaratilganda o'zi yozilgan joydagi o'zgaruvchilarga havolani "orqa cho'ntagiga" solib oladi. Shuning uchun tashqi funksiya ishini tugatgandan keyin ham ichki funksiya uning o'zgaruvchilarini o'qiy va o'zgartira oladi:
hisoblagichYarat()qaytarganoshir()har chaqiruvda o'shasonini oshiraveradi.
Bu darsda
- Closure nima ekanini va u qanday paydo bo'lishini o'z so'zingiz bilan tushuntira olasiz.
- Funksiya tugagach uning o'zgaruvchilari qachon yo'qolmasligini bilasiz.
- Closure qiymatning nusxasini emas, o'zgaruvchining o'zini eslashini kod bilan ko'rsata olasiz.
- Chrome DevTools'da funksiyaning
[[Scopes]]ini ochib, u nimani eslab qolganini ko'ra olasiz. vazifalardagikeyingiIdqanday buzilishi mumkinligini ko'rib, uni yashiradigan id generatorini yozasiz.
Oldin bilishingiz kerak: Scope asoslari, Callback va HOF, Execution context va call stack, Leksik scope va scope zanjiri, TDZ.
1. Nega bu kerak?
08-qismda uchta sir ochilmay qolgan edi.
Birinchisi — Scope asoslari darsidagi buyurtma hisoblagichi. Har yangi buyurtmaga raqam berish uchun soni ni funksiyadan tashqarida e'lon qilgan edik:
let soni = 0;
function buyurtmaQabul(manzil) {
soni++;
console.log(`${soni}-buyurtma: ${manzil}`);
}
buyurtmaQabul("Chilonzor");
buyurtmaQabul("Yunusobod");Konsolda:
1-buyurtma: Chilonzor
2-buyurtma: YunusobodKod ishlaydi, lekin soni global. Faylning istalgan joyidagi istalgan kod soni = 0 deb yozib, hisobni buzib qo'yishi mumkin. O'sha darsda "hisoblagichni yashirin saqlashning chiroyli usuli bor" degan edik.
Ikkinchisi — Callback va HOF darsidagi chegirmaYaratuvchi(20). U ishini tugatdi, lekin qaytgan funksiya keyinroq foiz ni baribir bildi. Qanday qilib?
Uchinchisi — 09-qismdagi tugmalar. Sikl ichida har tugmaga tinglovchi qo'shdik, va har biri o'z ishini esladi (Holatdan UI chizish, addEventListener). "Bu xususiyatning nomi — closure, uni 10-qismda chuqur o'rganamiz" degan edik.
Uchala sirning javobi bitta. Bugun uni ochamiz.
Hayotdan o'xshatish: Toshkentdan boshqa shaharga o'qishga ketgan talaba. U uydan chiqib ketdi, lekin cho'ntagida uy kalitini olib yuradi. Uy joyida turibdi — chunki kimdir uning kalitini olib yuribdi. Talaba xohlagan payt qaytib, xonasidagi narsalarni ola oladi, joyini o'zgartira oladi. Closure ham shunday: ichki funksiya tashqariga "ketadi", lekin o'zi tug'ilgan uyning kalitini olib ketadi.
Closure — JavaScript'ning eng muhim va eng ko'p so'raladigan mavzularidan biri. U birinchi o'qishda g'alati tuyulishi tabiiy. Shuning uchun bu darsda sekin yuramiz: avval sirni qadamma-qadam ochamiz, keyin qoidani aytamiz, oxirida DevTools'da o'z ko'zimiz bilan ko'ramiz. Keyingi ikki darsda esa closure'ni amalda ishlatamiz va uning tuzoqlarini o'rganamiz.
2. Sirni yaqindan ko'ramiz
2.1 Hisoblagich yasovchi funksiya
soni ni globaldan olib, funksiya ichiga qo'yamiz. Bu funksiya hisoblagichni yasaydi va uni qaytaradi:
function hisoblagichYarat() {
let soni = 0;
return function oshir() {
soni++;
return soni;
};
}
const oshir = hisoblagichYarat();
console.log(oshir()); // 1
console.log(oshir()); // 2
console.log(oshir()); // 3Qatorma-qator:
hisoblagichYarat()chaqirildi. Ichidalet soni = 0yaratildi.- U ichki
oshirfunksiyasini qaytardi — chaqirmadi, faqat qaytardi (qavssiz). hisoblagichYaratishini tugatdi. Biz qaytgan funksiyanioshiro'zgaruvchisiga saqladik.oshir()har chaqirilgandasonini bittaga oshirdi: 1, 2, 3.
To'rtinchi qadamni yana o'qing. soni — hisoblagichYarat ning lokal o'zgaruvchisi. Funksiya allaqachon tugagan. Scope asoslari darsida "funksiya tugasa, uning o'zgaruvchilari yo'qoladi" degan edik. Unda soni qayerda yashayapti? Va nega u har safar 0 dan boshlanmay, o'sib boryapti?
2.2 Tashqaridan tegib bo'lmaydi
Yana bir narsani tekshiramiz — soni ga tashqaridan murojaat qilish:
function hisoblagichYarat() {
let soni = 0;
return function oshir() {
soni++;
return soni;
};
}
const oshir = hisoblagichYarat();
oshir();
console.log(soni);ReferenceError: soni is not definedTarjimasi: "soni aniqlanmagan". Global muhitda soni degan nom yo'q — u hisoblagichYarat ning ichida. Demak soni ni faqat oshir ko'radi. Uni nolga qaytarib yoki -999 qilib buzadigan boshqa kod yo'q. «Nega bu kerak?» bo'limidagi global soni muammosi hal bo'ldi.
Ikki fakt yonma-yon turibdi: soni tirik (oshir uni o'zgartiryapti), lekin hech kimga ko'rinmaydi. Buni tushuntirish uchun "muhit" degan tushunchaga qaytamiz.
Tekshirib ko'ring:
hisoblagichYaratdagireturn function oshir() {...}nireturn oshir();deb yozsak (ichki funksiya alohida e'lon qilingan bo'lsa),const oshir = hisoblagichYarat()ga nima tushadi?
Javob
Funksiya emas, son — 1. oshir() qavs bilan yozilgani uchun u darhol chaqirildi va natijasi qaytdi. Keyin oshir() deb chaqirsangiz — TypeError: oshir is not a function. Funksiyani qaytarishda qavs qo'yilmaydi — xuddi callback'ni uzatishdagidek (Callback va HOF).
3. Muhit va "orqa cho'ntak"
3.1 Eslab olamiz: har chaqiruv — yangi muhit
Execution context va Leksik scope darslarida ko'rganmiz: funksiya chaqirilganda JavaScript unga yangi muhit yaratadi. Leksik muhit (lexical environment) — o'zgaruvchilar nomi va qiymati yozilgan "jadval" hamda tashqi muhitga havola. Kod biror nomni ishlatsa, JavaScript uni avval joriy muhitdan qidiradi. Topilmasa — tashqi muhitdan, undan ham tashqarisidan, global muhitgacha. Bu zanjir — scope zanjiri.
Odatda funksiya tugashi bilan uning muhiti keraksiz bo'lib qoladi va JavaScript uni xotiradan tozalaydi. Lekin "odatda" — har doim degani emas.
3.2 Funksiya tug'ilgan joyini eslaydi
Bitta yangi qoida qo'shiladi:
Funksiya yaratilganda u o'zi yaratilgan muhitga havolani o'zida saqlab oladi.
Spetsifikatsiyada (JavaScript qoidalari yozilgan rasmiy hujjatda) bu yashirin xususiyat [[Environment]] deb ataladi. Ikki qavatli kvadrat qavs — "ichki, koddan ko'rinmaydi" degani: oshir.Environment deb yozib, uni o'qib bo'lmaydi. Biz uni orqa cho'ntak deymiz: har funksiya orqa cho'ntagida o'zi tug'ilgan uyning kalitini olib yuradi.
Endi hisoblagichYarat ni qaytadan o'qiymiz:
hisoblagichYarat()chaqirildi — yangi muhit yaratildi, undasoni: 0.- Shu muhit ichida
oshirfunksiyasi yaratildi. U orqa cho'ntagiga shu muhitning kalitini soldi. hisoblagichYarattugadi. Odatda uning muhiti tozalanardi. Lekinoshirhali tirik va u muhitga havola qilib turibdi. JavaScript ishlatilayotgan narsani o'chirmaydi — muhit saqlanib qoladi.oshir()chaqirilganda, uning o'z yangi muhiti yaratiladi (u bo'sh —oshirda o'zgaruvchi yo'q). Bu yangi muhitning "tashqi muhiti" — orqa cho'ntakdagi muhit.sonishu yerdan topiladi.
Pastdagi Keyingi tugmasini bosib, har qadamda stek va muhitlarga qarang. Eng muhim lahza — 4-qadam: hisoblagichYarat stekdan chiqib ketadi, lekin soni yo'qolmaydi.
Diagrammada xuddi shu tuzilma, ikkinchi chaqiruvdan keyin. Uzluksiz strelka — har muhitning "tashqi muhiti" (qidiruv yo'li), punktir — funksiyaning orqa cho'ntagi:
flowchart LR
C["oshir() chaqiruvi muhiti<br/>(o'z o'zgaruvchisi yo'q)"] -- "tashqi muhit" --> H
O["oshir funksiyasi"] -. "orqa cho'ntak<br/>[[Environment]]" .-> H
H["hisoblagichYarat muhiti<br/>soni: 2"] -- "tashqi muhit" --> G["global muhit<br/>hisoblagichYarat, oshir"]Diagrammaga qarang: hisoblagichYarat ning chaqiruvi allaqachon yo'q, uning muhiti esa bor. Ikkalasi bir narsa emas. Chaqiruv — stekdagi vaqtinchalik yozuv, muhit — o'zgaruvchilar turadigan joy. Muhit kerak bo'lib turgan ekan, u yashaydi.
3.3 Closure'ning ta'rifi
Endi atamani aniq aytamiz.
Closure — funksiya va u yaratilgan leksik muhitning birikmasi. Inglizcha "close over" — "o'rab olmoq": funksiya o'z atrofidagi o'zgaruvchilarni "o'rab olib" ketadi. O'zbekcha tarjimasi yo'q — dasturchilar "closure" deb ishlatadi, biz ham shunday.
Ikki nozik joy bor:
- Texnik jihatdan JavaScript'dagi har bir funksiya closure. Hatto global funksiya ham global muhitni orqa cho'ntagida olib yuradi. Lekin bu oddiy holatda hech narsa sezilmaydi.
- Amalda "closure" deganda ichki funksiya tashqi funksiyadan uzoqroq yashagan holatni nazarda tutishadi: u qaytarildi, tinglovchi sifatida berildi yoki taymerga topshirildi. Aynan shunda "funksiya tugadi, lekin o'zgaruvchilari tirik" degan sehr ko'rinadi.
Endi 08-qismdagi chegirmaYaratuvchi sirining javobi tayyor:
function chegirmaYaratuvchi(foiz) {
return (narx) => narx - (narx * foiz) / 100;
}
const yangiYil = chegirmaYaratuvchi(20);
console.log(yangiYil(100000)); // 80000foiz — parametr, parametrlar ham muhitda turadi. Arrow funksiya yaratilganda orqa cho'ntagiga foiz: 20 turgan muhitni soldi. yangiYil(100000) chaqirilganda narx — o'z muhitida, foiz — orqa cho'ntakdagi muhitda topildi.
Uchinchi sir ham shu qoida bilan ochiladi. Holatdan UI chizish darsida for (const ish of holat.ishlar) sikli ichida () => ishniAlmashtir(ish.id) yozgan edik. Sikl har aylanishda yangi ish o'zgaruvchisini yaratadi: const — blokka tegishli, har aylanish esa — yangi blok. Har aylanishda yaratilgan arrow o'sha aylanishning muhitini cho'ntagiga soladi. Shuning uchun birinchi tugma birinchi ish ni, ikkinchisi ikkinchisini eslaydi.
Qisqasi, uchala sirning javobi:
- Hisoblagich —
soniichki funksiyaning orqa cho'ntagida yashaydi. chegirmaYaratuvchi—foizparametri ham muhitda, u ham cho'ntakka tushadi.- Tugmalar — har aylanish — yangi muhit, har tinglovchi o'zinikini eslaydi.
Tekshirib ko'ring: Quyidagi kodda closure bormi?
function salomBer() { const ism = "Ali"; console.log(ism); } salomBer();
Javob
Texnik jihatdan salomBer ham global muhitni eslaydi. Lekin amaldagi ma'nodagi closure yo'q: ichki funksiya yo'q, ism faqat chaqiruv davomida kerak va salomBer tugashi bilan uning muhiti tozalanadi. Closure "ko'rinadigan" bo'lishi uchun ichki funksiya tashqi funksiyadan uzoqroq yashashi kerak.
4. Har chaqiruv — yangi muhit
4.1 Ikki mustaqil hisoblagich
«Bahor» da ikki navbat bor: zalda o'tiradiganlar va olib ketadiganlar. Har biriga alohida raqam kerak. hisoblagichYarat ni ikki marta chaqiramiz:
function hisoblagichYarat() {
let soni = 0;
return function oshir() {
soni++;
return soni;
};
}
const zal = hisoblagichYarat();
const olibKetish = hisoblagichYarat();
console.log(zal()); // 1
console.log(zal()); // 2
console.log(olibKetish()); // 1
console.log(zal()); // 3olibKetish() 3 emas, 1 qaytardi. Sababi «Muhit va orqa cho'ntak» bo'limidagi qoidada: har chaqiruv yangi muhit yaratadi. hisoblagichYarat ikki marta chaqirildi — ikkita alohida muhit, ikkita alohida soni. zal birinchisining kalitini, olibKetish ikkinchisinikini olib yuradi. Ular bir-birining soni ini ko'rmaydi ham.
Xuddi ikki talabaning ikki xil uyi va ikki xil kaliti kabi. Kod bitta, muhitlar har chaqiruvda yangi.
4.2 Qaytgan funksiyani saqlamaslik
Shu qoidaning teskari tomoni ham bor. Bu kod nima chiqaradi?
function hisoblagichYarat() {
let soni = 0;
return function oshir() {
soni++;
return soni;
};
}
console.log(hisoblagichYarat()()); // 1
console.log(hisoblagichYarat()()); // 1
console.log(hisoblagichYarat()()); // 1hisoblagichYarat()() — "yasa va darhol chaqir". Har qatorda yangi hisoblagich yasaldi, bir marta chaqirildi va tashlab yuborildi. Uchta hisoblagich, har biri bir martadan ishladi. Hisob o'sishi uchun bitta hisoblagichni saqlab, uni qayta-qayta chaqirish kerak.
Tekshirib ko'ring:
const a = hisoblagichYarat(); const b = a;dan keyina(); a(); b();— oxirgi chaqiruv nima qaytaradi?
Javob
3. b = a yangi hisoblagich yasamadi — bitta funksiyaga ikkinchi nom berdi (Havola semantikasi). a va b — bitta funksiya, bitta orqa cho'ntak, bitta soni. Yangi muhit faqat hisoblagichYarat() chaqirilganda paydo bo'ladi.
5. Closure o'zgaruvchini eslaydi, nusxasini emas
5.1 Jonli bog'lanish
Boshlovchilarning eng keng tarqalgan fikri: "closure yaratilgan paytdagi qiymatning nusxasini oladi". Bu noto'g'ri. Closure muhitning o'zini eslaydi — demak, o'zgaruvchi keyin o'zgarsa, closure yangi qiymatni ko'radi:
let oshNarxi = 35000;
function narxniAyt() {
console.log(`Osh: ${oshNarxi} so'm`);
}
narxniAyt();
oshNarxi = 38000;
narxniAyt();Konsolda:
Osh: 35000 so'm
Osh: 38000 so'mnarxniAyt yaratilganda oshNarxi 35 000 edi. Lekin u "35 000" degan raqamni emas, oshNarxi turgan joyni esladi. Ikkinchi chaqiruvda o'sha joyga qaradi — u yerda endi 38 000.
O'xshatishga qaytsak: talaba uyning rasmini emas, kalitini olib yurgan. Uyda mebel o'zgarsa, qaytib kelganda yangi mebelni ko'radi.
5.2 Ikki funksiya — bitta muhit
Bitta muhitda ikkita funksiya tug'ilsa, ikkalasi bir xil kalitni oladi. Biri o'zgartirgan narsani ikkinchisi ko'radi:
function kassaYarat() {
let tushum = 0;
function sot(narx) {
tushum += narx;
}
function hisobot() {
console.log(`Bugungi tushum: ${tushum} so'm`);
}
return [sot, hisobot];
}
const [sot, hisobot] = kassaYarat();
sot(35000);
sot(5000);
hisobot(); // Bugungi tushum: 40000 so'mkassaYarat ikki funksiyani massivda qaytardi, biz ularni massiv destructuring bilan oldik. sot va hisobot bitta chaqiruvda, bitta muhitda tug'ilgan. Shuning uchun sot oshirgan tushum ni hisobot ko'radi. Tashqaridagi hech kim esa tushum ga tegolmaydi — faqat shu ikki funksiya orqali. Bu g'oyani keyingi darsda "private holat" deb ataymiz va ko'p ishlatamiz.
Tekshirib ko'ring: Quyidagi kod nima chiqaradi va nega?
let ism = "Ali"; const salom = () => console.log(ism); ism = "Vali"; salom();
Javob
Vali. Arrow funksiya ism ning qiymatini emas, ism o'zgaruvchisining o'zini eslaydi. Chaqiruv paytida ism da "Vali" turibdi.
6. Qaysi muhit: yozilgan joy, chaqirilgan joy emas
Orqa cho'ntakka funksiya yaratilgan muhit tushadi. Funksiya keyin qayerga olib borilsa ham, qayerda chaqirilsa ham — cho'ntakdagi kalit o'zgarmaydi. Bu — Leksik scope darsidagi qoidaning closure'dagi davomi:
const filial = "Chilonzor";
function manzilniAyt() {
console.log(`Filial: ${filial}`);
}
function boshqaJoydaChaqir() {
const filial = "Yunusobod";
manzilniAyt();
}
boshqaJoydaChaqir(); // Filial: ChilonzormanzilniAyt boshqaJoydaChaqir ichida chaqirildi va o'sha yerda filial = "Yunusobod" bor. Lekin natija — "Chilonzor". manzilniAyt global muhitda tug'ilgan, orqa cho'ntagida global muhit. Chaqirilgan joydagi filial uni qiziqtirmaydi.
Shuning uchun closure oldindan aytib beriladigan: funksiya qaysi o'zgaruvchilarni ko'rishini bilish uchun uning qayerda chaqirilishini qidirish shart emas. Kodda u qayerda yozilganiga qarash kifoya.
7. DevTools'da closure'ni ko'rish
7.1 [[Scopes]]
Orqa cho'ntakni Chrome'da ko'rsa bo'ladi. Quyidagi kodni sahifangizga qo'ying (yoki Console panelida bajaring) va console.dir bilan funksiyani oching:
function hisoblagichYarat() {
let soni = 0;
const katta = new Array(1000).fill(1);
return function oshir() {
soni++;
return soni;
};
}
const oshir = hisoblagichYarat();
console.dir(oshir);Chiqqan ƒ oshir() yonidagi uchburchakni bosing, eng pastda [[Scopes]] ni oching. Qisqartirilgan ko'rinishi:
ƒ oshir()
name: "oshir"
[[Scopes]]: Scopes[3]
0: Closure (hisoblagichYarat) {soni: 0}
1: Script {oshir: ƒ}
2: Global {window: Window, self: Window, document: document, …}Bu — scope zanjirining o'zi, ichkaridan tashqariga:
Closure (hisoblagichYarat)— orqa cho'ntak. Qavs ichida — qaysi funksiyaning muhiti. Ichidasoni: 0.oshir()ni bir necha marta chaqirib, yana oching — son o'sganini ko'rasiz.Script— oddiy<script>dagiconstvaletlar. Ular global, lekinwindowga yozilmaydi (Global obyekt vaglobalThisdarsida ko'ramiz).Global— global obyekt (window) va unga yozilgan hamma narsa.
Brauzer konsoli funksiyani ƒ oshir() deb ko'rsatadi, Node esa [Function: oshir] deydi — ma'nosi bir xil. [[Scopes]] ni esa faqat brauzer DevTools'i ko'rsatadi.
7.2 Hamma narsa eslab qolinmaydi
Yuqoridagi Closure ichida katta yo'q. Qiziq: katta ham hisoblagichYarat ning muhitida edi-ku?
Chrome'da kodni V8 degan JavaScript dvigateli (kodni bajaradigan ichki dastur) bajaradi. U bu yerda aqlli ish qiladi: ichki funksiyalar ishlatmaydigan o'zgaruvchini orqa cho'ntakka solmaydi. Bizning oshir faqat soni ni ishlatadi — shuning uchun faqat u saqlandi, 1000 elementli massiv esa tozalandi. Bu — tezlik uchun optimizatsiya, til qoidasi emas. Uning bitta muhim istisnosini Closure tuzoqlari darsida ko'ramiz: u xotira sizib chiqishiga olib kelishi mumkin.
7.3 To'xtash nuqtasida
Closure'ni yana bir joyda ko'rasiz — Sources panelida. oshir ichidagi soni++; qatoriga to'xtash nuqtasi (breakpoint) qo'ying va oshir() ni chaqiring (DevTools bilan debug). O'ng tomondagi Scope bo'limida to'rt qavat chiqadi: Local (oshir'ning o'z muhiti — bo'sh), Closure (hisoblagichYarat) (soni shu yerda), Script va Global. Qiymatni shu yerda ikki marta bosib o'zgartirsa ham bo'ladi.
Tekshirib ko'ring:
const zal = hisoblagichYarat(); zal(); zal();dan keyinconsole.dir(zal)qilsangiz,Closureichida nima bo'ladi?const olibKetish = hisoblagichYarat();ningClosureichida-chi?
Javob
zal da {soni: 2}, olibKetish da {soni: 0}. Ikki funksiya — ikki xil muhit. DevTools buni aniq ko'rsatadi: kod bir xil, [[Scopes]] dagi qiymatlar har xil.
8. Ko'p uchraydigan xatolar
8.1 Funksiya o'rniga natijani qaytarish
function hisoblagichYarat() {
let soni = 0;
function oshir() {
soni++;
return soni;
}
return oshir();
}
const oshir = hisoblagichYarat();
console.log(oshir());TypeError: oshir is not a functionTarjimasi: "oshir funksiya emas". return oshir() funksiyani chaqirib, natijasini — 1 sonini — qaytardi. oshir ga son tushdi, sonni esa chaqirib bo'lmaydi. Xato hisoblagichYarat da emas, keyingi qatorda chiqdi — shuning uchun avval "nima qaytardim?" deb qarang. Tuzatish: return oshir; — qavssiz.
8.2 Yasovchini har safar qayta chaqirish
«Har chaqiruv — yangi muhit» bo'limidagi hisoblagichYarat()() — xuddi shu xatoning qisqa shakli. Ko'pincha u funksiya ichida yashirinadi:
function hisoblagichYarat() {
let soni = 0;
return () => ++soni;
}
function buyurtmaQabul(manzil) {
const navbat = hisoblagichYarat();
console.log(`${navbat()}-buyurtma: ${manzil}`);
}
buyurtmaQabul("Chilonzor");
buyurtmaQabul("Yunusobod");Konsolda:
1-buyurtma: Chilonzor
1-buyurtma: YunusobodHar buyurtmada hisoblagichYarat() qayta chaqirildi — har safar yangi soni = 0. Tuzatish: hisoblagichni bir marta, funksiyadan tashqarida yasang: const navbat = hisoblagichYarat();, buyurtmaQabul ichida esa faqat navbat() ni chaqiring.
8.3 Tashqi o'zgaruvchini soyalash
function hisoblagichYarat() {
let soni = 0;
return function oshir() {
let soni = 0;
soni++;
return soni;
};
}
const oshir = hisoblagichYarat();
console.log(oshir()); // 1
console.log(oshir()); // 1Ichkarida yana let soni yozildi. Endi oshir o'z muhitida soni ni topadi va tashqisigacha bormaydi — soyalash (shadowing). Har chaqiruvda yangi 0. Tuzatish: ichki let ni olib tashlang — tashqi o'zgaruvchini yangilash uchun e'lon kerak emas.
8.4 "Closure nusxa oladi" deb o'ylash
«Jonli bog'lanish» bo'limidagi fikr. Agar closure yaratilgandan keyin o'zgaruvchi o'zgarsa, closure yangi qiymatni ko'radi. Bu ba'zan foydali, ba'zan esa kutilmagan natija beradi — ikkinchi holatni Closure tuzoqlari darsida "eskirgan closure" deb o'rganamiz.
9. Mashqlar
1-mashq (oson): Salomlashuvchi
salomlashuvchiYarat(salom) funksiyasini yozing. U ism qabul qiladigan funksiya qaytarsin. const ozbekcha = salomlashuvchiYarat("Assalomu alaykum"), keyin ozbekcha("Malika") → Assalomu alaykum, Malika!. Ikkinchi salomlashuvchini "Xush kelibsiz" bilan yasang.
Ishora: chegirmaYaratuvchi bilan bir xil qolip — parametr orqa cho'ntakda qoladi.
Yechim
function salomlashuvchiYarat(salom) {
return (ism) => `${salom}, ${ism}!`;
}
const ozbekcha = salomlashuvchiYarat("Assalomu alaykum");
const mehmonga = salomlashuvchiYarat("Xush kelibsiz");
console.log(ozbekcha("Malika")); // Assalomu alaykum, Malika!
console.log(mehmonga("Bobur")); // Xush kelibsiz, Bobur!Ikki chaqiruv — ikki muhit: birida salom "Assalomu alaykum", ikkinchisida "Xush kelibsiz". Har qaytgan arrow o'z muhitini eslaydi.
2-mashq (o'rta): Stol navbati
navbatYarat(harf) funksiyasini yozing. U har chaqirilganda keyingi navbat raqamini satr sifatida qaytaradigan funksiya bersin: "A-1", "A-2", ... Zal uchun "A", olib ketish uchun "B" navbatini yasab, aralash chaqiring.
Yechim
function navbatYarat(harf) {
let raqam = 0;
return () => {
raqam++;
return `${harf}-${raqam}`;
};
}
const zal = navbatYarat("A");
const olibKetish = navbatYarat("B");
console.log(zal()); // A-1
console.log(zal()); // A-2
console.log(olibKetish()); // B-1
console.log(zal()); // A-3Har muhitda ikki narsa: parametr harf va o'zgaruvchi raqam. harf faqat o'qiladi, raqam o'zgaradi — ikkalasi ham orqa cho'ntakda.
3-mashq (qiyin): Natijani oldindan ayting
Kodni ishga tushirmasdan, har console.log nima chiqarishini yozing. Keyin Node'da tekshiring.
let foiz = 10;
function chegirmaYarat() {
let qoldiq = 0;
return (narx) => {
const chegirma = (narx * foiz) / 100;
qoldiq += chegirma;
return `${narx - chegirma} (jami: ${qoldiq})`;
};
}
const kassa = chegirmaYarat();
console.log(kassa(30000));
foiz = 20;
console.log(kassa(30000));
const yangi = chegirmaYarat();
console.log(yangi(30000));Yechim
Konsolda:
27000 (jami: 3000)
24000 (jami: 9000)
24000 (jami: 6000)- Birinchi chaqiruvda
foiz— 10, chegirma 3 000,qoldiq3 000. foizglobal, va closure uning o'zini eslaydi. 20 bo'lgach,kassaham 20 ni ko'radi: chegirma 6 000,qoldiq3 000 + 6 000 = 9 000.yangi— yangi chaqiruv, yangiqoldiq = 0.foizesa hamon o'sha global — 20. Shuning uchun 6 000.
Bu mashqda scope zanjirining ikki qavati ham ishladi: qoldiq — chegirmaYarat muhitidan (har chaqiruvda yangi), foiz — global muhitdan (hammaga bitta).
4-mashq: Vazifalar qadami — keyingiId ni kim buzishi mumkin?
vazifalar da yangi vazifaning id si holat.keyingiId dan olinadi. holat esa oddiy obyekt — uni istalgan kod o'zgartira oladi. Bu xavfni o'z ko'zingiz bilan ko'ring, keyin closure bilan yechimni Node'da tayyorlang. Ilovaning o'ziga keyingi darsda ulaymiz.
1. Buzib ko'ring. Saytni Live Server'da oching, DevTools Console'da yozing:
holat.keyingiId = 1;So'ng forma orqali "Choy damlash" vazifasini qo'shing va uning katagini belgilang. Nima bo'ldi?
2. Generatorni yozing. Node'da idYasovchiYarat(boshlangich) funksiyasini yozing. U har chaqirilganda keyingi id ni qaytaradigan funksiya bersin: avval boshlangich ning o'zi, keyin har safar bittaga ko'proq. Hisoblagichga tashqaridan tegib bo'lmasligini tekshiring.
Yechim
1. Yangi vazifa ham id: 1 oldi — endi ro'yxatda ikkita id: 1 bor. "Choy damlash" katagi bosilganda u belgilanmaydi, buning o'rniga "Non olish" belgilanadi! Tinglovchi id ni o'qib, vazifaniTop(1) ni chaqiradi, find esa birinchi topilganini qaytaradi. Xuddi shunday, "Choy damlash" ni o'chirsangiz — "Non olish" o'chadi. Bitta tasodifiy qator butun ilovani noto'g'ri ishlatdi.
Bu safar uni biz ataylab buzdik. Lekin katta ilovada holat ni o'nlab funksiya o'zgartiradi — ulardan bittasidagi xato yetarli.
2.
function idYasovchiYarat(boshlangich) {
let keyingi = boshlangich;
return () => keyingi++;
}
const yangiId = idYasovchiYarat(4);
console.log(yangiId()); // 4
console.log(yangiId()); // 5
console.log(yangiId()); // 6
console.log(typeof keyingi); // undefinedkeyingi++ — avval joriy qiymatni qaytaradi, keyin oshiradi (Arifmetik operatorlar). Shuning uchun birinchi id — 4, ya'ni boshlangich ning o'zi. keyingi faqat qaytgan arrow'ning orqa cho'ntagida: tashqaridan uni na o'qib, na o'zgartirib bo'ladi. Ko'pi bilan yangiId() ni ortiqcha chaqirib, bitta raqamni "o'tkazib yuborish" mumkin — lekin takroriy id berish endi imkonsiz.
10. Real ishda
- React hook'lari. React'dagi
useState,useEffectva boshqa hook'lar closure ustiga qurilgan. Komponent ichidagi har tugma bosish funksiyasi o'sha chizishdagi holatni eslab qoladi. React'ni 17-qismda o'rganamiz — u yerda bu dars sizga ko'p yordam beradi. - Hodisa tinglovchilari. 09-qismda yozgan har bir
addEventListener(..., () => {...})— closure: arrow tashqi o'zgaruvchilarni (tugmani, holatni) eslaydi va oradan soatlar o'tib bosilganda ham ularni topadi. - Kutubxonalar. Lodash'dagi
debounce,once,memoizekabi yordamchilar ichida closure turadi. Ularni keyingi darsda o'zimiz yozamiz. - Intervyu. "Closure nima?" — JavaScript intervyusining eng mashhur savoli. Yaxshi javob uch qismdan iborat: ta'rif (funksiya + leksik muhit), misol (hisoblagich) va foydasi (yashirin holat, fabrikalar). Bonus — "u qiymatni emas, o'zgaruvchini eslaydi" va DevTools'dagi
[[Scopes]].
Xulosa
- Funksiya yaratilganda o'zi yaratilgan muhitni "orqa cho'ntagiga" soladi (
[[Environment]]). - Closure — funksiya va shu muhitning birikmasi. Muhit kerak bo'lib turgan ekan, tashqi funksiya tugasa ham u yashaydi.
- Tashqi funksiyaning har chaqiruvi — yangi muhit: ikki hisoblagich bir-biriga xalaqit bermaydi.
- Closure qiymatning nusxasini emas, o'zgaruvchining o'zini eslaydi. Bitta muhitda tug'ilgan funksiyalar uni bo'lishadi.
- Qaysi muhit eslanishi funksiya yozilgan joyga bog'liq, chaqirilgan joyga emas.
- Chrome DevTools:
console.dir(fn)→[[Scopes]]→Closure (...); Sources'da — Scope bo'limi.
Keyingi dars: Closure amaliyotda — closure bilan private holat, fabrika funksiyalar, once, oddiy kesh va debounce ning o'zini yozamiz, vazifalar ga esa id generatorini ulaymiz.
Manbalar
- MDN: "Closures" — developer.mozilla.org
- ECMAScript 2025 Language Specification: "Environment Records", "[[Environment]]" (OrdinaryFunctionCreate) — tc39.es/ecma262
- Chrome for Developers: "JavaScript debugging reference" — Scope pane — developer.chrome.com/docs/devtools
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!