Closure — nima va qanday ishlaydi

~20 daq · JavaScript · Funksiyalar chuqurroq

Muammodan boshlaylik

function hisoblagich() {
  let son = 0;
  return function() {
    son++;
    return son;
  };
}

const hisoblash = hisoblagich();
console.log(hisoblash()); // 1
console.log(hisoblash()); // 2
console.log(hisoblash()); // 3

hisoblagich funksiyasi tugadi — lekin son o'zgaruvchisi hali ham tirik. Qaytarilgan funksiya uni "eslab qoldi". Bu — closure.


Closure nima?

Closure — funksiya o'zi yaratilgan scope dagi o'zgaruvchilarni "eslab qolishi". Funksiya tashqi scope tugagandan keyin ham o'sha o'zgaruvchilarga murojaat qila oladi.

Hayotiy o'xshatma: xat. Siz do'stingizga xat yozdingiz — xatda o'sha paytdagi his-tuyg'ularingiz, voqealar bor. Yillar o'tgach xatni o'qisangiz — o'sha paytdagi "kontekst" xatda saqlanib qolgan. Closure — funksiyaning o'z "xati".


Qanday ishlaydi?

Funksiya yaratilganda — u o'zining scope zanjirini "yopib oladi" (closes over). Tashqi funksiya tugagandan keyin ham ichki funksiya tashqi o'zgaruvchilarga murojaat qila oladi.

function tashqi() {
  const xabar = "Salom!"; // ← tashqi o'zgaruvchi

  function ichki() {
    console.log(xabar); // ← closure — xabar ni eslab qoldi
  }

  return ichki;
}

const fn = tashqi(); // tashqi() tugadi
fn(); // "Salom!" — lekin xabar hali tirik!

tashqi() tugagandan keyin xabar o'chirilishi kerak edi — lekin ichki funksiyasi uni ishlatayotgani uchun xotiradan o'chirilmaydi.


Nima uchun shunday?

Nima uchun closure kerak?

Closure — private holat yaratish imkonini beradi. Ob'ektga o'xshash, lekin tashqaridan to'g'ridan-to'g'ri o'zgartirib bo'lmaydi.

function hisoblagich(boshlangich = 0) {
  let son = boshlangich; // ← tashqaridan ko'rinmaydi

  return {
    oshir: () => ++son,
    kamayt: () => --son,
    qiymat: () => son,
  };
}

const c = hisoblagich(10);
c.oshir();  // 11
c.oshir();  // 12
c.kamayt(); // 11
c.qiymat(); // 11

// son ga to'g'ridan-to'g'ri murojaat qilib bo'lmaydi:
c.son; // undefined — himoyalangan

Amaliy misollar

Bir marta bajariladigan funksiya

function birMarta(fn) {
  let bajarildi = false;
  let natija;

  return function(...args) {
    if (!bajarildi) {
      natija = fn(...args);
      bajarildi = true;
    }
    return natija;
  };
}

const initialize = birMarta(() => {
  console.log("Faqat bir marta!");
  return 42;
});

initialize(); // "Faqat bir marta!" → 42
initialize(); // 42 — xabar chiqmaydi
initialize(); // 42

Parametr bilan funksiya yaratish (factory)

function ko'paytiruvchi(n) {
  return x => x * n; // ← n ni eslab qoladi
}

const ikki_baravar = ko'paytiruvchi(2);
const uch_baravar = ko'paytiruvchi(3);

ikki_baravar(5); // 10
uch_baravar(5);  // 15

Memoization — natijani keshlashtirish

function memoize(fn) {
  const kesh = {}; // ← closure — kesh saqlanib qoladi

  return function(x) {
    if (kesh[x] !== undefined) {
      return kesh[x]; // ← keshdan qaytaradi
    }
    kesh[x] = fn(x);
    return kesh[x];
  };
}

const sekinHisoblash = memoize(n => {
  // og'ir hisoblash...
  return n * n;
});

sekinHisoblash(5); // hisoblaydi → 25
sekinHisoblash(5); // keshdan → 25 (tez)

Tez-tez uchraydigan xatolar

var bilan sikl va closure:

// ❌ Klassik xato
const funksiyalar = [];
for (var i = 0; i < 3; i++) {
  funksiyalar.push(() => console.log(i));
}
funksiyalar[0](); // 3 — kutilgan: 0
funksiyalar[1](); // 3 — kutilgan: 1
funksiyalar[2](); // 3 — kutilgan: 2

Nima uchun? var funksiya scope — barcha funksiyalar bir xil i ga murojaat qiladi. Sikl tugaganda i = 3.

// ✅ let bilan — har iteratsiyada yangi i
for (let i = 0; i < 3; i++) {
  funksiyalar.push(() => console.log(i));
}
funksiyalar[0](); // 0 ✅
funksiyalar[1](); // 1 ✅
funksiyalar[2](); // 2 ✅

Xotira sizib chiqishi (memory leak):

function kattaMaʼlumot() {
  const kattaMassiv = new Array(1000000).fill("x"); // ← katta xotira

  return function() {
    return kattaMassiv[0]; // ← faqat birinchi element kerak
    // lekin butun massiv xotiradan o'chirilmaydi!
  };
}

Closure tashqi o'zgaruvchini tirik saqlaydi — kerak bo'lmasa null ga o'rnatiladi:

let fn = kattaMaʼlumot();
fn(); // ishlatildi
fn = null; // ← xotira bo'shatiladi

Xulosa

Closure — funksiya o'zi yaratilgan scope dagi o'zgaruvchilarni eslab qolishi. Tashqi funksiya tugagandan keyin ham ishlaydi.

Asosiy ishlatilishi:

  • Private holat — tashqaridan o'zgartirib bo'lmaydigan o'zgaruvchilar
  • Factory funksiyalar — parametr bilan funksiya yaratish
  • Memoization — natijalarni keshlash
  • Bir marta bajariladigan funksiyalar

var bilan sikl + closure — klassik xato. let ishlatiladi.


Keyingi dars

12-dars: Higher Order Functions — funksiyani parametr va qaytish qiymati sifatida