Funksiyalar bilan chuqur ishlash

O'zgaruvchi ko'lami, closure

JavaScript funksiyalar tili sanaladi. Bu unga katta moslashuvchanlik beradi: funksiyalarni yaratish, o'zgaruvchiga berish, boshqa funksiyaga uzatish va keyinroq chaqirish mumkin. Buni tushunish uchun o'zgaruvchi ko'lami va closure tushunchalarini o'rganamiz.

Blok ko'lami (block scope)

let va const orqali e'lon qilingan o'zgaruvchi faqat o'zi joylashgan blok ichida (figurali qavslar { } orasida) ko'rinadi. Blokdan tashqarida u mavjud emas:

block-scope.js
// natija shu yerda chiqadi

Xuddi shunday, if, for, while bloklari ham o'z ko'lamiga ega. for siklidagi hisoblagich i faqat sikl ichida yashaydi:

for-scope.js
// natija shu yerda chiqadi
Blok ko'lami โ€” kodni xavfsizroq qiladi. O'zgaruvchi faqat kerak bo'lgan joyda yashaydi va tasodifan boshqa joyni buzib qo'ymaydi.

Ichki funksiyalar va tashqi o'zgaruvchilar

Funksiya ichida boshqa funksiya (ichki, nested funksiya) e'lon qilinishi mumkin. Eng muhimi โ€” ichki funksiya tashqi funksiyaning o'zgaruvchilariga kira oladi:

nested-fn.js
// natija shu yerda chiqadi

Ichki funksiya tashqi o'zgaruvchini nafaqat o'qishi, balki o'zgartirishi ham mumkin. Bu oddiy ko'rinsa-da, closure'ning asosini tashkil etadi.

Leksik muhit (lexical environment) โ€” soddalashtirib

JavaScript ichkarida o'zgaruvchilarni qanday izlaydi? Buni tushunish uchun leksik muhit tushunchasi kerak. Uni soddalashtirib shunday tasavvur qiling:

O'zgaruvchi ishlatilganda, JavaScript uni avval joriy muhitdan qidiradi. Topmasa โ€” tashqi muhitga chiqadi, u yerdan ham topmasa yana tashqariga... shu tariqa eng tashqi (global) muhitga qadar. Bu "tashqariga qarab qidirish" zanjiri closure'ning kalitidir.

Muhim tafsilot: o'zgaruvchining qaysi tashqi muhitga bog'lanishi funksiya qayerda YOZILGANI bilan aniqlanadi ("leksik" so'zi shundan), qayerda chaqirilgani bilan emas. Ya'ni bog'lanish kod matnida qanday joylashganiga qarab, oldindan belgilanadi.

Closure nima?

Closure (yopilma) โ€” bu tashqi o'zgaruvchilarni eslab qoladigan va ularga kira oladigan funksiya. JavaScript'da deyarli barcha funksiyalar closure hisoblanadi โ€” ular yaratilgan joydagi tashqi o'zgaruvchilarni "eslab qoladi".

Sehr shundaki: tashqi funksiya ishlab bo'lgach ham, uning o'zgaruvchilari yo'qolmaydi โ€” agar ularga ichki funksiya hali ham ishora qilib turgan bo'lsa. Quyidagi misolda buni ko'ramiz:

closure-basic.js
// natija shu yerda chiqadi

makeGreeter('Ali') chaqiruvi tugagan bo'lsa ham, qaytarilgan funksiya o'zining name = 'Ali' qiymatini eslab turadi. greetAli va greetVali โ€” bir-biridan mustaqil, chunki har biri o'z leksik muhitini saqlaydi.

Hisoblagich (counter) misoli

Closure'ning eng klassik va foydali misoli โ€” hisoblagich. Har chaqirilganda keyingi sonni qaytaruvchi funksiya yasaymiz. Hisob count tashqarida yashiringan, unga faqat qaytarilgan funksiya orqali kirish mumkin:

counter.js
// natija shu yerda chiqadi

Har chaqiruvda count qiymati saqlanib, ortib boradi. Bu shuni ko'rsatadiki, count o'zgaruvchisi bitta va o'sha โ€” funksiya uni haqiqatan eslab turibdi, har safar noldan boshlamaydi.

Har bir makeCounter() chaqiruvi esa butunlay yangi, mustaqil hisoblagich yaratadi:

two-counters.js
// natija shu yerda chiqadi

Amaliy foyda

Closure nazariya emas โ€” u har kuni ishlatiladigan amaliy vosita:

Sozlanadigan funksiya fabrikasi misoli โ€” berilgan koeffitsientga ko'paytiruvchi funksiyalar yasaymiz:

multiplier.js
// natija shu yerda chiqadi
Agar funksiya boshqa funksiyani qaytarsa yoki argument sifatida qabul qilsa โ€” ehtimol siz closure ishlatayotgansiz. Bu JavaScript'ning eng kuchli xususiyatlaridan biri, uni yaxshi tushunish katta ustunlik beradi.

Xulosa