إيه هو الـ Closure أصلاً؟ 🤔
الـ Closure من أهم المفاهيم في JavaScript، وهو موجود من أول ما اللغة ظهرت في 1995. الفكرة ظهرت عشان تحل مشكلة كبيرة: إزاي نحافظ على البيانات private في اللغة، خصوصاً إن JavaScript ماكانش فيها classes ولا private variables في الأول.
تعريف بسيط 📚
الـ Closure ببساطة هو لما فانكشن داخلية “تفتكر” المتغيرات اللي كانت موجودة في الفانكشن الأم وقت تعريفها، حتى بعد ما الفانكشن الأم تخلص شغلها. دي ميزة قوية جداً بتخلينا نعمل حاجات كتير مهمة في الـ JavaScript.
ليه الـ Closure مهم؟ 🎯
- Data Privacy: بيساعدنا نخلي البيانات private ومانخليش حد يوصلها من برة
- State Management: بيحافظ على state بين الـ function calls
- Module Pattern: بيساعدنا نعمل modules منظمة في الكود
- Event Handlers: بيستخدم كتير في الـ callbacks والـ event handlers
مثال تفصيلي للفهم 🔍
خلينا نفهم الـ Closure بمثال بسيط ونشرحه خطوة خطوة:
function outerFunction(x) {
let name = "Ahmed"; // متغير private في الفانكشن الخارجي
function innerFunction() {
// الفانكشن الداخلي بيكون عنده "closure" على المتغيرات المحيطة
console.log(name); // يقدر يوصل للمتغير name
console.log(x); // ويقدر يوصل للباراميتر x
}
return innerFunction; // بنرجع الفانكشن الداخلي
}
const myFunction = outerFunction("مرحباً");
myFunction(); // هيطبع: Ahmed و مرحباً
// لو حاولنا نوصل للمتغير name من برة
console.log(name); // ⚠️ Error: name is not defined
شرح المثال خطوة خطوة 📝
- إنشاء الـ Scope:
- لما بننفذ
outerFunction
، بيتعمل scope جديد فيه المتغيرname
والباراميترx
- المتغيرات دي private ومش موجودة برة الـ function
- لما بننفذ
┌─── Global Scope ──────────────────┐
│ │
│ ┌─── outerFunction Scope ───┐ │
│ │ │ │
│ │ let name = "Ahmed" │ │
│ │ parameter x = "مرحباً" │ │
│ │ │ │
│ │ ┌─ innerFunction ─┐ │ │
│ │ │ │ │ │
│ │ │ closure │ │ │
│ │ │ ↑ name │ │ │
│ │ │ ↑ x │ │ │
│ │ └─────────────────┘ │ │
│ └───────────────────────────┘ │
└───────────────────────────────────┘
- الـ Closure في العمل:
- الـ
innerFunction
بتعمل closure على الـ scope بتاعouterFunction
- يعني بتحتفظ بمرجع (reference) للمتغيرات اللي محتاجاها (
name
وx
) - حتى بعد ما
outerFunction
تخلص، الـ closure بيفضل محتفظ بالقيم دي
- الـ
┌─── بعد تنفيذ outerFunction ───────┐
│ │
│ myFunction ──────┐ │
│ ↓ │
│ ┌─── Closure ───────────────┐ │
│ │ │ │
│ │ name = "Ahmed" │ │
│ │ x = "مرحباً" │ │
│ │ │ │
│ │ function() { │ │
│ │ console.log(name) │ │
│ │ console.log(x) │ │
│ │ } │ │
│ └───────────────────────────┘ │
└───────────────────────────────────┘
- الـ Lexical Scoping:
- JavaScript بيستخدم نظام الـ Lexical Scoping
- يعني الفانكشن بتقدر توصل للمتغيرات:
- اللي معرفة جواها
- واللي معرفة في أي scope برة منها
- لكن مش العكس! الـ scope الخارجي مش بيقدر يوصل للمتغيرات جوة الـ functions
┌─── Lexical Scoping ──────────────────────┐
│ │
│ Global Scope │
│ │ │
│ ├─── outerFunction │
│ │ │ │
│ │ ├─── name, x │
│ │ │ (متاح للـ innerFunction) │
│ │ │ │
│ │ └─── innerFunction │
│ │ (يقدر يوصل للمتغيرات فوق) │
│ │ │
└──────────────────────────────────────────┘
الـ Lexical Scope والـ Closure: العلاقة بينهم 🤝
إيه هو الـ Lexical Scope? 🔍
الـ Lexical Scope هو القواعد اللي بتحدد: “مين يقدر يشوف مين؟” في الكود. يعني زي خريطة بتقول للفانكشن: “أنت ممكن تشوف المتغيرات دي”.
// مثال بسيط للـ Lexical Scope
let name = "Ahmed"; // متغير في الـ global scope
function sayHi() {
let greeting = "Hi"; // متغير في الـ local scope
console.log(greeting + " " + name); // يقدر يشوف الاتنين
}
sayHi(); // "Hi Ahmed"
// console.log(greeting); // ❌ Error! مش هيشتغل
العلاقة بين الـ Lexical Scope والـ Closure 🔄
الـ Lexical Scope هو الأساس اللي بيخلي الـ Closure يشتغل! تخيل الموضوع كده:
- الـ Lexical Scope بيحدد: “مين يقدر يشوف مين؟”
- الـ Closure بيقول: “طيب خليني أحفظ اللي أنا شايفه ده”
function createGame() {
let score = 0; // متغير في الـ lexical scope بتاع createGame
// الفانكشن دي عندها closure على score
function play() {
score++; // بتقدر تشوف وتعدل score بسبب الـ lexical scope
console.log(`Score: ${score}`);
}
return play;
}
const game = createGame();
game(); // Score: 1
game(); // Score: 2
تشبيه للفهم 📦
تخيل الموضوع كده:
┌─── المكتب (Global Scope) ───────────┐
│ │
│ ┌─── الدرج (Lexical Scope) ───┐ │
│ │ │ │
│ │ المتغيرات │ │
│ │ - score │ │
│ │ │ │
│ │ ┌─── Closure ─────┐ │ │
│ │ │ │ │ │
│ │ │ "أنا فاكر كل │ │ │
│ │ │ حاجة في الدرج" │ │ │
│ │ │ │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────────────┘ │
└────────────────────────────────────┘
النقط المهمة 🎯
الـ Lexical Scope:
- بيحدد نطاق رؤية المتغيرات
- ثابت ومعروف وقت كتابة الكود
- بيتحكم في مين يقدر يشوف مين
الـ Closure:
- بيستفيد من الـ Lexical Scope
- بيحفظ المتغيرات اللي هو شايفها
- بيخلي المتغيرات دي private
- بيفضل فاكر القيم حتى بعد ما الفانكشن الأصلية تخلص
العلاقة بينهم:
- الـ Lexical Scope هو الأساس
- الـ Closure هو اللي بيستخدم الأساس ده
- مع بعض بيعملوا نظام قوي للـ data privacy والـ state management
إزاي الـ Closure بيشتغل ؟ 🔧
1. الـ Closure في الميموري 🧠
تخيل معايا إن الـ Closure زي صندوق بيحتفظ بحاجتين:
- الفانكشن نفسها
- كل المتغيرات اللي الفانكشن محتاجاها من برة
function makeCounter() {
let x = 1;
let y = 2;
return function counter() {
// بنستخدم المتغيرات
return x + y;
};
}
// لما نعمل كده:
const counter = makeCounter();
في الميموري، بيتعمل صندوق كده:
┌─── صندوق الـ Closure ───┐
│ │
│ ┌── المتغيرات ────┐ │
│ │ x = 1 │ │
│ │ y = 2 │ │
│ └────────────────┘ │
│ │
│ ┌── الفانكشن ─────┐ │
│ │ counter() │ │
│ │ {return x + y} │ │
│ └────────────────┘ │
└─────────────────────────┘
2. ليه الصندوق ده مهم؟ 🤔
const counter1 = makeCounter();
const counter2 = makeCounter();
counter1(); // 3
counter2(); // 3
كل ما نعمل counter جديد، بيتعمل صندوق جديد:
┌─── counter1 ───┐ ┌─── counter2 ───┐
│ x = 1 │ │ x = 1 │
│ y = 2 │ │ y = 2 │
│ counter() │ │ counter() │
└───────────────┘ └───────────────┘
3. إزاي بيشتغل في الميموري؟ 💭
function greet(name) {
let message = "مرحباً "; // متغير محلي
return function() {
// بيستخدم المتغيرات اللي برة
console.log(message + name);
}
}
const sayHiAhmed = greet("أحمد");
لما الكود ده بيشتغل:
greet
بتتنفذ وبتعمل المتغيرات بتاعتها- قبل ما ترجع الفانكشن، بتعمل “صورة” للمتغيرات اللي هنحتاجها
- الفانكشن اللي راجعة بتاخد معاها الصورة دي
قبل Return:
┌─── greet ───────────┐
│ message = "مرحباً" │
│ name = "أحمد" │
└──────────────────┘
بعد Return:
┌─── sayHiAhmed ──────┐
│ "صورة" المتغيرات: │
│ message = "مرحباً" │
│ name = "أحمد" │
└──────────────────┘
4. إيه اللي بيحصل لو عدلنا المتغيرات؟ 🔄
function createUpdater() {
let value = 0;
return {
getValue: function() { return value; },
increment: function() { value++; },
updateValue: function(newValue) { value = newValue; }
};
}
const updater = createUpdater();
كل الفانكشنز بتشترك في نفس النسخة من value
:
┌─── updater ────────────────┐
│ │
│ value = 0 │
│ │ │
│ ├─► getValue() │
│ ├─► increment() │
│ └─► updateValue() │
│ │
└────────────────────────────┘
لما نستخدمه:
updater.getValue(); // 0
updater.increment(); // value بقت 1
updater.updateValue(5);// value بقت 5
updater.getValue(); // 5
الـ Closure في الـ JavaScript Engine 🛠️
أ. خطوات تنفيذ الـ Closure في الـ V8 Engine
function createAuthSystem() {
let token = null;
let user = null;
return {
login(username, password) {
// محاكاة عملية تسجيل الدخول
token = `token_${username}_${Date.now()}`;
user = { username, lastLogin: new Date() };
return true;
},
isAuthenticated() {
return token !== null;
},
getUserInfo() {
if (!token) return null;
return { ...user };
},
logout() {
token = null;
user = null;
}
};
}
const auth = createAuthSystem();
auth.login('ahmed', '123456');
console.log(auth.isAuthenticated()); // true
console.log(auth.getUserInfo()); // { username: 'ahmed', lastLogin: ... }
تعالوا نفهم بالظبط إزاي الـ V8 Engine بيتعامل مع الـ Closure من خلال المثال العملي اللي فات دا:
- مرحلة التحليل (Parsing) 📝 المحرك بيقرأ الكود ويحلله:
┌── النطاق العام (Global) ─────────────────┐
│ │
│ ┌── نطاق createAuthSystem ──────────────┐ │
│ │ │ │
│ │ token = null │ │
│ │ user = null │ │
│ │ │ │ │
│ │ └─► login() │ │
│ │ - بيقدر يقرأ token │ │
│ │ - بيقدر يعدل token │ │
│ │ - بيقدر يقرأ user │ │
│ │ - بيقدر يعدل user │ │
│ │ │ │
│ │ ┌─► isAuthenticated() │ │
│ │ │ - بيقدر يقرأ token │ │
│ │ └───────────────────────────────┘ │
│ │ │ │
│ │ ┌─► getUserInfo() │ │
│ │ │ - بيقدر يقرأ token │ │
│ │ │ - بيقدر يقرأ user │ │
│ │ └───────────────────────────────┘ │
│ │ │ │
│ │ ┌─► logout() │ │
│ │ │ - بيقدر يعدل token │ │
│ │ │ - بيقدر يعدل user │ │
│ │ └───────────────────────────────┘ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────┘
- مرحلة إنشاء النطاقات (Scope Creation) 🏗️
// بناء سلسلة النطاقات
{
scopes: {
login: {
// login بيشوف كل حاجة في createAuthSystem
parent: 'createAuthSystem',
type: 'function',
variables: []
},
isAuthenticated: {
// isAuthenticated بيشوف كل حاجة في createAuthSystem
parent: 'createAuthSystem',
type: 'function',
variables: []
},
getUserInfo: {
// getUserInfo بيشوف كل حاجة في createAuthSystem
parent: 'createAuthSystem',
type: 'function',
variables: []
},
logout: {
// logout بيشوف كل حاجة في createAuthSystem
parent: 'createAuthSystem',
type: 'function',
variables: []
},
createAuthSystem: {
// createAuthSystem بيشوف النطاق العام
parent: 'global',
type: 'function',
variables: ['token', 'user', 'login', 'isAuthenticated', 'getUserInfo', 'logout']
}
}
}
- مرحلة تجهيز البيئة (Environment Setup) ⚙️
// تجهيز بيئة التشغيل
{
environments: {
createAuthSystem: {
variables: {
token: {
value: null,
type: 'string',
scope: 'private'
},
user: {
value: null,
type: 'object',
scope: 'private'
}
},
closure: {
login: {
capturedVars: ['token', 'user']
},
isAuthenticated: {
capturedVars: ['token']
},
getUserInfo: {
capturedVars: ['token', 'user']
},
logout: {
capturedVars: ['token', 'user']
}
}
}
}
}
- مرحلة التنفيذ (Execution) ⚡️
┌── ذاكرة التشغيل ──────────────────────────┐
│ │
│ ┌── بيئة login ──────────────────┐ │
│ │ │ │
│ │ ┌── المتغيرات المحمية ──────┐ │ │
│ │ │ token = null │ │ │
│ │ │ user = null │ │ │
│ │ └───────────────────────────┘ │ │
│ │ │ │
│ │ ┌── النطاق ──────────────────┐ │ │
│ │ │ createAuthSystem │ │ │
│ │ │ ↓ │ │ │
│ │ │ global │ │ │
│ │ └──────────────────────────────┘ │ │
│ └────────────────────────────────────┘ │
│ │
│ ┌── بيئة isAuthenticated ──────┐ │
│ │ │ │
│ │ ┌── المتغيرات المحمية ──────┐ │ │
│ │ │ token = null │ │ │
│ │ └───────────────────────────┘ │ │
│ │ │ │
│ │ ┌── النطاق ──────────────────┐ │ │
│ │ │ createAuthSystem │ │ │
│ │ │ ↓ │ │ │
│ │ │ global │ │ │
│ │ └──────────────────────────────┘ │ │
│ └────────────────────────────────────┘ │
│ │
│ ┌── بيئة getUserInfo ────────┐ │
│ │ │ │
│ │ ┌── المتغيرات المحمية ──────┐ │ │
│ │ │ token = null │ │ │
│ │ │ user = null │ │ │
│ │ └───────────────────────────┘ │ │
│ │ │ │
│ │ ┌── النطاق ──────────────────┐ │ │
│ │ │ createAuthSystem │ │ │
│ │ │ ↓ │ │ │
│ │ │ global │ │ │
│ │ └──────────────────────────────┘ │ │
│ └────────────────────────────────────┘ │
│ │
│ ┌── بيئة logout ──────────────┐ │
│ │ │ │
│ │ ┌── المتغيرات المحمية ──────┐ │ │
│ │ │ token = null │ │ │
│ │ │ user = null │ │ │
│ │ └───────────────────────────┘ │ │
│ │ │ │
│ │ ┌── النطاق ──────────────────┐ │ │
│ │ │ createAuthSystem │ │ │
│ │ │ ↓ │ │ │
│ │ │ global │ │ │
│ │ └──────────────────────────────┘ │ │
│ └────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
أمثلة عملية من العالم الحقيقي 🌍
١. نظام المصادقة (Authentication System)
function createAuthSystem() {
let token = null;
let user = null;
return {
login(username, password) {
// محاكاة عملية تسجيل الدخول
token = `token_${username}_${Date.now()}`;
user = { username, lastLogin: new Date() };
return true;
},
isAuthenticated() {
return token !== null;
},
getUserInfo() {
if (!token) return null;
return { ...user };
},
logout() {
token = null;
user = null;
}
};
}
const auth = createAuthSystem();
auth.login('ahmed', '123456');
console.log(auth.isAuthenticated()); // true
console.log(auth.getUserInfo()); // { username: 'ahmed', lastLogin: ... }
٢. نظام إدارة الحالة (State Management)
function createAppState() {
const state = {
theme: 'light',
language: 'ar',
notifications: []
};
const listeners = new Set();
return {
getState() {
return { ...state };
},
setState(updates) {
Object.assign(state, updates);
listeners.forEach(listener => listener(state));
},
subscribe(listener) {
listeners.add(listener);
return () => listeners.delete(listener);
}
};
}
const appState = createAppState();
const unsubscribe = appState.subscribe(state => {
console.log('التحديث:', state);
});
appState.setState({ theme: 'dark' }); // التحديث: { theme: 'dark', ... }
٣. مكتبة لإدارة النماذج (Form Library)
function createFormManager(initialValues = {}) {
let values = { ...initialValues };
let errors = {};
let touched = {};
return {
setValue(field, value) {
values[field] = value;
touched[field] = true;
this.validate(field);
},
getValue(field) {
return values[field];
},
validate(field) {
const value = values[field];
errors[field] = !value ? 'هذا الحقل مطلوب' : '';
},
getErrors() {
return { ...errors };
},
isValid() {
return Object.values(errors).every(error => !error);
},
reset() {
values = { ...initialValues };
errors = {};
touched = {};
}
};
}
const form = createFormManager({ username: '', email: '' });
form.setValue('username', 'ahmed');
console.log(form.getValue('username')); // 'ahmed'
console.log(form.isValid()); // false (email still empty)
٤. نظام التخزين المؤقت الذكي (Smart Caching System)
function createSmartCache() {
const cache = new Map();
const expiryTimes = new Map();
// تنظيف الكاش كل دقيقة
setInterval(() => {
const now = Date.now();
for (const [key, expiry] of expiryTimes) {
if (now > expiry) {
cache.delete(key);
expiryTimes.delete(key);
}
}
}, 60000);
return {
set(key, value, ttlMinutes = 5) {
cache.set(key, value);
expiryTimes.set(key, Date.now() + (ttlMinutes * 60000));
},
get(key) {
if (!cache.has(key)) return null;
if (Date.now() > expiryTimes.get(key)) {
this.delete(key);
return null;
}
return cache.get(key);
},
delete(key) {
cache.delete(key);
expiryTimes.delete(key);
},
clear() {
cache.clear();
expiryTimes.clear();
}
};
}
const cache = createSmartCache();
cache.set('user:123', { name: 'Ahmed' }, 10); // تخزين لمدة 10 دقائق
console.log(cache.get('user:123')); // { name: 'Ahmed' }
٥. نظام إدارة الأحداث المتقدم (Advanced Event System)
function createEventManager() {
const events = new Map();
const onceCallbacks = new Set();
return {
on(event, callback) {
if (!events.has(event)) {
events.set(event, new Set());
}
events.get(event).add(callback);
// إرجاع دالة لإلغاء الاشتراك
return () => this.off(event, callback);
},
once(event, callback) {
const wrappedCallback = (...args) => {
callback(...args);
this.off(event, wrappedCallback);
};
onceCallbacks.add(wrappedCallback);
return this.on(event, wrappedCallback);
},
off(event, callback) {
if (events.has(event)) {
events.get(event).delete(callback);
onceCallbacks.delete(callback);
}
},
emit(event, ...args) {
if (events.has(event)) {
events.get(event).forEach(callback => {
try {
callback(...args);
} catch (error) {
console.error(`خطأ في معالج الحدث ${event}:`, error);
}
});
}
},
clear() {
events.clear();
onceCallbacks.clear();
}
};
}
const eventManager = createEventManager();
eventManager.on('userLogin', user => {
console.log('تم تسجيل دخول:', user);
});
eventManager.once('appStart', () => {
console.log('تم بدء التطبيق - هذه الرسالة تظهر مرة واحدة فقط');
});
eventManager.emit('userLogin', { id: 1, name: 'Ahmed' });
eventManager.emit('appStart');
eventManager.emit('appStart'); // لن تظهر الرسالة مرة أخرى
هذه الأمثلة توضح كيف يمكن استخدام الـ Closure في تطبيقات حقيقية لتحقيق:
- Data Encapsulation
- Information Hiding
- State Management
- Smart Caching
- Advanced Event System