المستوى المطلوب: مبتدئ. لو شغّلت JavaScript قبل كده وفهمت معنى الدالة والمتغيّر، الكلام ده ليك. مش مطلوب منك تعرف لا async ولا prototypes ولا حتى ES Modules.
هتفهم في المقال ده ليه الدالة الجوّانية بتفتكر متغيّرات الدالة الخارجية حتى بعد ما الخارجية خلصت شغلها. ده مش تفصيلة أكاديمية — ده اللي بيخلّي React hooks تشتغل، وبيخلّي مكتبات زي Lodash تقدر تعمل debounce و throttle، وبيخلّيك تكتب counter آمن من غير ما حد يعدّل قيمته من بره.
Closures في JavaScript: المتغيّرات اللي مش بتموت لمّا الدالة تخلص
المشكلة باختصار
أغلب المبتدئين بيفترضوا إن لمّا الدالة تخلص تنفيذها، كل المتغيّرات اللي اتعرّفت جوّاها بتتمسح من الذاكرة. ده افتراض منطقي ومعقول. والمشكلة إنه غلط في حالة واحدة بالظبط: لمّا الدالة بترجع دالة تانية جوّاها بتستخدم متغيّرات الأم.
الكود ده مثلاً، لو سألتك هيطبع كام، إيه إجابتك؟
function makeGreeter(name) {
return function () {
console.log("أهلاً يا " + name);
};
}
const greet = makeGreeter("حايس");
greet();
الإجابة الحدسية: undefined أو خطأ، لأن name اتعرّف داخل makeGreeter اللي خلصت تنفيذها بالفعل. الإجابة الفعلية: "أهلاً يا حايس". ده Closure.
المثال أولاً: درج المكتب
تخيّل إن عندك مكتب فيه 5 أدراج. كل درج جوّاه ورقة مكتوب عليها رقم. الأدراج كلها تابعة للمكتب — لو الشركة قرّرت تشيل المكتب، الأدراج بتتشال معاه طبيعي.
دلوقتي تخيّل سيناريو غريب: ضفت لمكتبك ساعة حائط معلّقة فوقه، والساعة دي بتقرأ الرقم اللي في الدرج الأوّل وتظهره. لمّا الشركة شالت المكتب، الساعة فضلت معلّقة على الحيط، ولمّا حد سألها "إيه الرقم؟" قالتله الرقم اللي كان في الدرج الأول. ليه؟ لأن الساعة "فاكرة" مكان الدرج، ومادام في حد فاكر، الدرج عمره ما هيتشال فعلياً من المخزن.
الساعة هنا = الدالة الجوّانية. الدرج = المتغيّر الخارجي. المكتب = الدالة الخارجية. مفيش حد قادر يحذف الأدراج طول ما الساعة لسه شغّالة.
التعريف العلمي الدقيق
الـ Closure هو دالة مرتبطة بـ الـ lexical environment اللي اتعرّفت فيه. الـ lexical environment ده هيكل بيانات داخلي في محرّك JavaScript بيتخزّن فيه: المتغيّرات المعرّفة في النطاق ده + مرجع للنطاق الأب (parent scope reference). الترتيب ده بيكوّن سلسلة اسمها scope chain.
لمّا الدالة الخارجية بترجع دالة جوّانية بتستخدم متغيّرات من الأم، محرّك JavaScript بيحطّ علامة على الـ environment ده تقول "ممنوع الـ garbage collector يلمسك". الـ environment بيفضل في الذاكرة طول ما في reference نشط للدالة الجوّانية.