01 Closure nədir?
Closure JavaScript-in ən güclü və maraqlı xüsusiyyətlərindən biridir. Closure funksiyaya yaradıldığı leksik mühitə (scope) çatmaq imkanı verir, hətta xarici funksiya icrasını başa vurduqdan sonra belə.
Sadə closure nümunəsi
Gəlin ən sadə closure nümunəsinə baxaq:
function outerFunction() {
let outerVariable = "I'm from outer";
function innerFunction() {
console.log(outerVariable); // Xarici dəyişənə çatır
}
return innerFunction;
}
const myFunction = outerFunction();
myFunction(); // I'm from outerBu necə işləyir:
- outerFunction icra olunur və innerFunction-u qaytarır
- outerFunction icrasını bitirir, amma onun dəyişənləri (outerVariable) yaddaşda qalır
- myFunction əslində innerFunction-dur
- myFunction çağırılanda o, hələ də outerVariable-a çata bilir (closure vasitəsilə)
Closure-lar necə işləyir
Hər closure öz yaradıldığı dəyişənlərin ayrı kopiyasını saxlayır:
function createGreeting(name) {
return function() {
console.log("Hello, " + name + "!");
};
}
const greetJohn = createGreeting("John");
const greetMary = createGreeting("Mary");
greetJohn(); // Hello, John!
greetMary(); // Hello, Mary!
// Hər funksiya öz name parametrini saxlayır02 Leksik mühit
Closure-ları daha yaxşı başa düşmək üçün leksik mühit (lexical environment) anlayışını bilmək lazımdır.
Leksik mühitin komponentləri
1. Environment Record
Bu hissədə bütün lokal dəyişənlər və funksiya parametrləri saxlanır.
2. Outer lexical environment istinadı
Bu istinad xarici (parent) scope-a yönəlir və scope chain yaradır.
Leksik mühit nümunəsi
Gəlin leksik mühitin necə işlədiyinə baxaq:
function outer() {
let a = 10;
let b = 20;
function middle() {
let c = 30;
function inner() {
let d = 40;
// İç-içə scope-lardan bütün dəyişənlərə çatmaq mümkündür
console.log(a + b + c + d); // 100
}
return inner;
}
return middle;
}
const middleFunc = outer();
const innerFunc = middleFunc();
innerFunc(); // 10003 Scope chain persistence
Closure-ların ən güclü xüsusiyyətlərindən biri onların scope chain-i saxlamasıdır. Bu o deməkdir ki, daxili funksiya xarici funksiyanın dəyişənlərinə həmişə çata bilir.
Scope-un saxlanması
Xarici funksiya icrasını bitirdikdən sonra belə, onun dəyişənləri closure vasitəsilə əlçatan qalır:
function createCounter() {
let count = 0; // Private dəyişən
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 1
counter(); // 2
counter(); // 3
// count dəyişəni saxlanılır və artırılırÇoxsaylı closure-lar
Hər dəfə xarici funksiya çağırılanda yeni closure yaranır və ayrı dəyişənlər saxlayır:
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
console.log(count);
},
decrement: function() {
count--;
console.log(count);
},
getCount: function() {
return count;
}
};
}
const counter1 = createCounter();
const counter2 = createCounter();
counter1.increment(); // 1
counter1.increment(); // 2
counter2.increment(); // 1
console.log(counter1.getCount()); // 2
console.log(counter2.getCount()); // 104 Closure-ların praktik istifadəsi
Closure-lar JavaScript-də çox istifadə olunan pattern-lərdir. Gəlin əsas istifadə hallarına baxaq:
1. Data privacy (məlumatın gizliliyi)
Closure-lar private dəyişənlər yaratmaq üçün istifadə olunur - xaricdən birbaşa dəyişdirilə bilməyən dəyişənlər:
function createBankAccount(initialBalance) {
let balance = initialBalance; // Private dəyişən
return {
deposit: function deposit(amount) {
if (amount > 0) {
balance += amount;
console.log("Deposited: " + amount + ". Balance: " + balance);
}
},
withdraw: function withdraw(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
console.log("Withdrawn: " + amount + ". Balance: " + balance);
} else {
console.log("Insufficient funds");
}
},
getBalance: function getBalance() {
return balance;
}
};
}
const myAccount = createBankAccount(1000);
myAccount.deposit(500); // Deposited: 500. Balance: 1500
myAccount.withdraw(200); // Withdrawn: 200. Balance: 1300
console.log(myAccount.getBalance()); // 1300
// console.log(myAccount.balance); // undefined - birbaşa çatmaq mümkün deyil!Faydalar:
- balance dəyişəni private-dır və birbaşa dəyişdirilə bilməz
- Yalnız metodlar vasitəsilə balans-a çatmaq və onu dəyişmək mümkündür
- Bu, data integrity və təhlükəsizliyi təmin edir
2. Factory functions
Closure-lar parametrlərə əsaslanan fərqli davranışa malik funksiyalar yaratmaq üçün istifadə olunur:
function createMultiplier(multiplier) {
return function multiply(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
const quadruple = createMultiplier(4);
console.log(double(5)); // 10
console.log(triple(5)); // 15
console.log(quadruple(5)); // 2005 IIFE (Immediately Invoked Function Expression)
IIFE yaradıldığı anda dərhal icra olunan funksiyadır. Bu, JavaScript-də çox istifadə olunan pattern-dir.
IIFE sintaksisi
IIFE yaratmağın iki əsas yolu var:
// Pattern 1: Funksiya mötərizədə, çağırış xaricində
(function() {
console.log("I run immediately!");
})();
// Pattern 2: Funksiya və çağırış hər ikisi mötərizədə
(function() {
console.log("I also run immediately!");
}());
// Parametrlərlə IIFE
(function(name) {
console.log("Hello, " + name + "!");
})("World");
// Private scope yaradır
(function() {
const message = "This is private";
console.log(message);
})();
// console.log(message); // ReferenceError: message is not definedHər iki pattern eyni işi görür:
(function() {...})()- Funksiya ifadəsi mötərizələrlə əhatə olunur(function() {...}())- Funksiya ifadəsi mötərizələrlə əhatə olunur və çağırış mötərizələri də daxildir
Nə üçün IIFE istifadə edirik?
IIFE-nin bir neçə vacib istifadə halı var:
- Scope izolasiyası - IIFE daxilindəki dəyişənlər xaricdən əlçatan olmur
- Global scope-u çirkləndirməmək - Global dəyişənlərin sayını azaltmaq
- Data privacy - Private dəyişənlər və metodlar yaratmaq
- Təkrar istifadə olunmayan kod - Bir dəfə icra olunmalı olan kod üçün ideal
// IIFE-siz: global scope-u çirkləndirir
var counter = 0;
function increment() {
counter++;
}
function getCounter() {
return counter;
}
// Bütün dəyişənlər və funksiyalar global-dır
// IIFE ilə: təmiz və encapsulated
const counterModule = (function() {
let counter = 0; // Private
return {
increment: function() {
counter++;
},
getCounter: function() {
return counter;
}
};
})();
counterModule.increment();
console.log(counterModule.getCounter()); // 1
// console.log(counter); // ReferenceError: counter is not definedParametrlərlə IIFE
IIFE-yə parametr ötürmək mümkündür. Bu, xarici dəyişənləri lokal scope-a təhlükəsiz şəkildə gətirmək üçün istifadə olunur.
// Arqumentlərin ötürülməsi
(function(name, age) {
console.log("Name: " + name + ", Age: " + age);
})("John", 30);
// Global dəyişəni IIFE-yə ötürmək
const globalVar = "I'm global";
(function(global) {
console.log(global); // I'm global
})(globalVar);06 Closure-larla bağlı tipik səhvlər
Closure-lar güclü mexanizmdir, amma bir neçə tipik səhv var ki, onlardan qaçmaq lazımdır:
❌ Loop-larda closure problemi
Ən çox rast gəlinən closure səhvi loop-larda baş verir:
// ❌ Problem: Hər dəfə 4 çıxarır
for (var i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i); // 4, 4, 4
}, i * 1000);
}
// Çünki var function scope-a malikdir və hər bir iterasiyada yeni i yaranır✅ Həll yolları:
// Həll 1: let istifadə edin (ən sadə)
for (let i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i); // 1, 2, 3 (düzgün)
}, i * 1000);
}
// Həll 2: IIFE istifadə edin
for (var i = 1; i <= 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j); // 1, 2, 3 (düzgün)
}, j * 1000);
})(i);
}
// Həll 3: Closure factory
for (var i = 1; i <= 3; i++) {
setTimeout((function(j) {
return function() {
console.log(j); // 1, 2, 3 (düzgün)
};
})(i), i * 1000);
}❌ Yaddaş sızması (memory leak)
Closure-lar lazım olmayan böyük obyektləri yaddaşda saxlaya bilər:
function createHeavyObject() {
const hugeArray = new Array(1000000).fill('data');
return function() {
// hugeArray bütünlüklə yaddaşda qala bilər.
console.log(hugeArray.length);
};
}
// Qaytarılan funksiya mövcud olduğu müddətdə yaddaşda qalacaq
const func = createHeavyObject();
// Daha yaxşı yol:
function createLightObject() {
const hugeArray = new Array(1000000).fill('data');
const length = hugeArray.length; // Yalnız lazım olanı saxlayın
return function() {
console.log(length); // İndi yalnız length saxlanılır, array yox
};
}07 Closure-ların yaddaşa təsiri
Closure-lar güclüdür, amma onların yaddaşa təsirini başa düşmək vacibdir.
Closure-lar yaddaşı necə təsir edir
- Closure yarandıqda, o öz leksik mühitinə istinad saxlayır
- Nəzəri olaraq, closure istifadə etdiyi bütün dəyişənlər yaddaşda qalır
- Müasir JavaScript engine-ləri çox vaxt istifadə olunmayan dəyişənləri optimizasiya edə bilər, lakin bu zəmanət deyil
- Əmin olmaq üçün yalnız lazım olan dəyərləri closure-da saxlayın
function outer() {
const largeData = new Array(10000).fill('data');
const smallData = 'small';
return function inner() {
// inner funksiyası yalnız smallData istifadə edir,
// amma closure öz leksik mühitinə istinad saxlayır
console.log(smallData);
};
}
const func = outer();
// Nəzəri olaraq largeData da yaddaşda qala bilər (engine-dən asılıdır)
// Qeyd: Müasir JavaScript engine-ləri (V8, SpiderMonkey) çox vaxt istifadə olunmayan dəyişənləri optimizasiya edir və silir. Lakin bu zəmanət deyil və bütün engine-lərdə işləməyə bilər.
// Daha yaxşı və zəmanətli yanaşma:
function betterOuter() {
const largeData = new Array(10000).fill('data');
const result = largeData.length; // Lazım olan dəyəri çıxarın və ayrıca saxlayın
return function inner() {
console.log(result); // İndi yalnız result (primitive dəyər) saxlanılır və largeData garbage collection tərəfindən silinə bilər
};
}Best practices
Yalnız lazım olanı saxlayın
Əgər böyük obyektdən yalnız bir dəyər lazımdırsa, onu ayrıca dəyişənə çıxarın.
Closure-ları həddindən artıq istifadə etməyin
Hər closure yaddaş istifadə edir. Çoxsaylı closure-lar performansa təsir edə bilər.
Lazım olmayan istinadları təmizləyin
Əgər closure-a daha ehtiyac yoxdursa, ona olan istinadı null-a təyin edin.
08 IIFE-yə müasir alternativlər
ES6 və sonrakı versiyalar IIFE-nin yerini tuta biləcək yeni xüsusiyyətlər təqdim etdi.
Block scope let/const ilə
Sadə scope izolasiyası üçün block scope istifadə edə bilərsiniz:
// Köhnə üsul: IIFE
(function() {
var temp = 42;
console.log(temp);
})();
// Müasir üsul: Block scope
{
let temp = 42;
console.log(temp);
}
// console.log(temp); // ReferenceError: temp is not definedES6 modulları
Module pattern-in yerini ES6 modulları tutdu:
// Köhnə üsul: IIFE module pattern
const myModule = (function() {
let privateVar = 'private';
return {
publicMethod: function() {
console.log(privateVar);
}
};
})();
// Müasir üsul: ES6 modules
// counter.js
let count = 0;
export function increment() {
count++;
}
export function getCount() {
return count;
}
// main.js
// import { increment, getCount } from './counter.js';IIFE-ni hələ də nə vaxt istifadə etmək olar
- Köhnə browser-ləri dəstəkləmək lazım olduqda
- Module sistemi olmayan mühitlərdə
- Bir dəfə icra olunmalı olan initialization kodu üçün
- Köhnə kod bazalarını support edərkən
Xülasə
Təklifiniz var, səhv və ya xəta tapdınız? Zəhmət olmasa bizimlə əlaqə saxlayın.