Seriya: JavaScript Basics Dars 19

IIFE

IIFE nima, nega qavslar kerak, V8 ichida qanday ishlaydi va Module Pattern, loop + var muammosi kabi real ishlatilishlari.

19-dars

IIFE

IIFE — Immediately Invoked Function Expression. Tarjimasi: Yaratilgan zahoti chaqiriladigan funksiya ifodasi.

Ikkita so'z muhim — Expression va Immediately Invoked. Nima uchun aynan shu ikki so'z — tushuntirib beraman.


Avval — Declaration vs Expression farqi

JS da funksiya yozishning ikki yo'li:

// 1. Function Declaration:
function foo() {
  console.log('foo');
}

// 2. Function Expression:
const bar = function() {
  console.log('bar');
};
  • Declaration — bu statement. U hech narsa qaytarmaydi, faqat funksiyani e'lon qiladi.
  • Expression — bu qiymat qaytaradi. O'zgaruvchiga berilishi, argument bo'lishi, darhol chaqirilishi mumkin.

Bu farq IIFE uchun juda muhim — hozir ko'rasiz.


IIFE ning asosiy shakli

(function() {
  console.log('IIFE ishladi');
})();

Bu yerda ikkita qism bor:

(function() {        // 1-qism: funksiya ifodasi (expression)
  console.log('...');
})                    // qavslar funksiyani "expression"ga aylantirdi

();                   // 2-qism: darhol chaqirish

Nega qavslar kerak — bu eng muhim joy

Qavslar faqat chiroyli ko'rinish uchun emas. Ularsiz kod xato beradi:

function() {    // SyntaxError!
  console.log('ishlaydi');
}();

Nima bo'ldi? V8 function kalit so'zini ko'rganida darhol Declaration deb o'ylaydi. Declaration esa:

  • Nom bo'lishi shart — function dan keyin nom yo'q → SyntaxError
  • Hech narsa qaytarmaydi — darhol chaqirib bo'lmaydi

Qavslar () qo'shilganda V8 ning "ko'zi" o'zgaradi:

(function() { ... })

V8 ( ko'rganida — "bu expression boshlanmoqda" deb tushunadi. Endi ichidagi function() {} Declaration emas, Expression sifatida qaraladi. Expression esa qiymat qaytaradi — bu qiymatni () bilan darhol chaqirish mumkin.


V8 ichida nima bo'ladi

(function() {
  const x = 10;
  console.log(x);
})();

Step 1 — (function() { ... }) — yangi funksiya obyekti yaratiladi, heap da saqlanadi. Hech qanday nom yo'q — bu anonim funksiya.

Step 2 — () — darhol chaqiriladi. Yangi Execution Context yaratiladi:

Execution Context: IIFE
LexicalEnvironment: { x: 10 }
ThisBinding: global / undefined
[[Environment]] → Global Execution Context

Step 3 — IIFE tugaydi. Execution Context Call Stack dan o'chiriladi. x yo'qoladi — tashqaridan hech kim uni ko'ra olmaydi, ko'rolmagan ham.


IIFE ning asosiy maqsadi — Scope izolyatsiyasi

var x = 'global';

(function() {
  var x = 'local'; // bu x tashqariga chiqmaydi
  console.log(x);  // 'local'
})();

console.log(x); // 'global' — tashqi x o'zgarmadi

var — function scope. IIFE yangi function scope yaratadi. Shuning uchun ichidagi var x tashqi var x ga hech qanday ta'sir qilmaydi.


Nega IIFE kerak edi — tarixiy sabab

ES6 (let, const, modullar) kelgunga qadar JS da block scope yo'q edi. Faqat var bor edi, u esa function scope.

// ES6 dan oldin bu ikkisi bir xil o'zgaruvchi:
{
  var secret = 'ko\'rinadi';
}
console.log(secret); // 'ko\'rinadi' — block scope yo'q!

Yagona yechim — yangi function scope yaratish, ya'ni IIFE:

(function() {
  var secret = 'ko\'rinmaydi';
})();

console.log(secret); // ReferenceError — tashqaridan yetib bo'lmaydi

IIFE ning turlari

1. Anonim IIFE (klassik):

(function() {
  // ...
})();

2. Nomli IIFE (stack trace uchun qulay):

(function init() {
  // ...
})();

Nom faqat funksiya ichidan ko'rinadi, tashqaridan emas. Debugging da stack trace da nom ko'rinishi uchun ishlatiladi.

3. Arrow function bilan (ES6+):

(() => {
  // ...
})();

4. Argument qabul qiluvchi IIFE:

(function(name, age) {
  console.log(`${name}, ${age} yoshda`);
})('Ali', 25);
//   ↑      ↑
// arg1   arg2 — darhol berildi

Bu pattern — tashqi o'zgaruvchilarni IIFE ga aniq, nazorat ostida kiritish uchun ishlatilgan. Global o'zgaruvchi bevosita ishlatish o'rniga, argument sifatida beriladi — bu kodni tahmin qilinadigan va testlanadigan qiladi.


Amaliy — IIFE ning real ishlatilishi

1. Module Pattern — ES6 modullaridan oldingi davr

const Counter = (function() {
  let count = 0; // private — tashqaridan yetib bo'lmaydi

  return {
    increment() { count++; },
    decrement() { count--; },
    getCount()  { return count; }
  };
})();

Counter.increment();
Counter.increment();
Counter.getCount(); // 2

Counter.count; // undefined — private

Bu yerda IIFE + Closure birga ishlaydi:

  • IIFE — yangi scope yaratdi
  • Closure — count ni qaytarilgan obyektning metodlari ushlab qoldi
  • Natija — count faqat metodlar orqali o'zgartiriladi

2. Loop va var muammosi — IIFE yechimi

// Muammo:
for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // 3, 3, 3
  }, 100);
}

// IIFE yechimi:
for (var i = 0; i < 3; i++) {
  (function(j) {       // har iteratsiyada yangi scope
    setTimeout(function() {
      console.log(j);  // 0, 1, 2
    }, 100);
  })(i);                // i ning o'sha paytdagi qiymati j ga berildi
}

Closure mavzusida ko'rdingiz bu misolni — IIFE har iteratsiyada yangi scope yaratadi, i ning o'sha paytdagi qiymatini j sifatida o'sha scope da saqlaydi.

3. Global namespace ifloslantirmaslik

Katta loyihalarda, ayniqsa script tag bilan yuklanadigan kutubxonalarda:

// IIFE ichida yozilgan — global scope ga hech narsa chiqmaydi
(function() {
  var _private = [];
  var _config  = {};

  function _helper() { /* ... */ }

  // faqat bitta nom global scope ga chiqadi:
  window.MyLibrary = {
    doSomething() { /* ... */ }
  };
})();

jQuery, lodash, va boshqa mashhur kutubxonalar aynan shu pattern bilan yozilgan.


IIFE hozir ham kerakmi

ES6 dan keyin:

  • let, const — block scope beradi
  • ES Modules — har fayl o'z scope da
  • Bundlerlar (webpack, vite) — scope izolyatsiyasini avtomatik qiladi

Shuning uchun yangi kodda IIFE kamdan-kam ishlatiladi. Lekin bilish muhim, chunki:

  • Eski kod bazalarida (jQuery davri) hamma joyda bor
  • Closure + Scope tushunchalarini mustahkamlaydi
  • "Nega ES6 modullar scope izolyatsiya qiladi" savoliga javob shu yerda