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 —
functiondan 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 —
countni qaytarilgan obyektning metodlari ushlab qoldi - Natija —
countfaqat 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