مستوى المقال: مبتدئ. لو لسه ماسك JavaScript من شوية وسمعت كلمة Closure وحسّيت إنها سحر، المقال ده هيخليها أوضح حاجة عندك في اللغة.
لو دالة في JavaScript رجّعتلك رقم بيزيد كل مرة تناديها — رغم إن الدالة الأصلية خلصت وانتهت — انت بالظبط قدام الـ Closure. ده مش bug ولا حاجة غريبة. ده أهم مفهوم في اللغة، ولو فهمته هتكتب كود أنضف وتبطّل تخاف من الأخطاء اللي ملهاش معنى.
Closures في JavaScript: الدالة اللي بتفتكر
المشكلة باختصار
المتغيّر اللي بتعرّفه جوه دالة المفروض يموت لما الدالة تخلّص شغلها. ده السلوك الطبيعي. لكن في JavaScript، لو دالة جوّانية رجعت وهي لسه "ماسكة" المتغيّر ده، المتغيّر بيفضل عايش. الكومبيوتر بيحتفظ بيه في الذاكرة عشان الدالة الجوّانية لسه محتاجاه. ده اللي اسمه Closure.
ليه ده مهم؟ لإنه أساس حاجات بتستخدمها كل يوم: العدّادات، الـ event handlers، الـ private variables، وأغلب أدوات الـ state في React. ومن غير ما تفهمه، هتقابل أخطاء مش هتعرف تفسّرها.
المثال البسيط الأول: خزانة المدرسة
تخيّل إن في مدرسة، كل طالب بياخد خزانة (locker) ليها مفتاح واحد بس. الطالب يمشي من المدرسة، يروح بيته، يرجع تاني السنة الجاية — الخزانة لسه فيها كتبه، والمفتاح لسه شغّال. المدرسة (النطاق الخارجي) مقفلت أبوابها، بس خزانتك انت لسه محفوظة لإنك ماسك مفتاحها.
الدالة الجوّانية هي الطالب. المتغيّر هو الخزانة. والمفتاح هو الـ Closure: الرابط اللي بيخلّي الطالب يوصل لخزانته حتى بعد ما الدوام خلص. باقي الطلاب مش هيقدروا يفتحوا خزانتك — كل واحد له خزانته لوحده. خليك فاكر النقطة دي، هي مهمة في آخر المقال.
التعريف العلمي الدقيق
بعد ما المثال وضّح الفكرة، خلّينا ندقّق. الـ Closure حسب توثيق Mozilla MDN هو: "تركيبة من دالة مع البيئة المعجمية (Lexical Environment) اللي اتعرّفت جواها الدالة دي". يعني الدالة بتحمل معاها مرجع لكل المتغيّرات اللي كانت متاحة لحظة تعريفها، مش لحظة استدعائها.
"المعجمي" (Lexical) هنا معناها: النطاق بيتحدّد من مكان كتابة الكود في الملف، مش من مكان مناداته. ده فرق جوهري. JavaScript بتقرر مين بيشوف مين بناءً على شكل الكود وانت بتكتبه، قبل ما يشتغل أصلاً. المواصفة الرسمية ECMAScript بتسمّي ده Lexical Scoping، وهو نفس المبدأ في لغات زي Python و Swift.
الكود الشغّال: عدّاد بيفتكر
ده أبسط Closure ممكن تكتبه. جرّبه على Node 22 أو في console المتصفح مباشرة:
function createCounter() {
let count = 0; // ده "الخزانة"
return function () {
count = count + 1; // الدالة الجوّانية ماسكة المفتاح
return count;
};
}
const next = createCounter();
console.log(next()); // 1
console.log(next()); // 2
console.log(next()); // 3