هذا المقال يتطلب مستوى: متوسط
لو فهمت الإغلاق صح، هتقدر تعمل متغيّرات خاصة، عدّادات، وكاش من غير ما تلوّث النطاق العام، وهتوقف باجات في الحلقات كانت بتضيّع عليك ساعات. المقال ده بيوضّح الفكرة بمثال بسيط، بعدين علميًا، وبيوريك الفخ اللي بيقع فيه أغلب الناس.
الإغلاقات في جافاسكريبت: الدالة اللي بتفتكر
الإغلاق (Closure) مش ميزة نادرة بتستخدمها مرة في السنة. ده أساس شغّال في كل callback، كل event handler، وكل دالة بترجّع دالة. الافتراض هنا إنك عارف الفرق بين تعريف الدالة واستدعائها، وشايف الكود بيتنفّذ في المتصفح أو Node.
المشكلة باختصار
لو عملت متغيّر جوّه دالة، المفروض إنه يتمسح لما الدالة تخلّص. لكن في حالات المتغيّر بيفضل عايش وبتتغيّر قيمته مع كل استدعاء، وإنت مش فاهم مين ماسكه. ده مش عطل، ده إغلاق شغّال. وفهمه بيحوّل سلوك غريب لأداة قوية.
الفكرة بمثال بسيط قبل التعريف العلمي
تخيّل موظف طلع من المكتب ومعاه حقيبة. جوّه الحقيبة ورقة مكتوب عليها رقم. المكتب اتقفل وراح، بس الموظف لسه ماسك الحقيبة. كل ما تسأله "الرقم كام؟" يفتح الحقيبة ويزوّده واحد ويقولّك النتيجة. المكتب (النطاق) خلص، لكن الورقة (المتغيّر) لسه محفوظة معاه لأنه شايلها في حقيبته.
ده بالظبط الإغلاق. الحقيبة هي "البيئة المعجمية" (Lexical Environment) اللي الدالة الداخلية بتشيلها معاها. دلوقتي التعريف الدقيق: الإغلاق هو دالة مربوطة بالمراجع الخاصة بالمتغيّرات اللي في نطاقها المحيط وقت تعريفها، فتظل قادرة على قراءتها وتعديلها حتى بعد انتهاء تنفيذ الدالة الأم. الربط بيتحدد بمكان كتابة الكود (lexical scope)، مش بمكان استدعائه.
مثال تنفيذي: عدّاد بحالة خاصة
الكود ده بيعمل عدّاد. المتغيّر count مش ظاهر برّه، محدش يقدر يعدّله غير عن طريق الدالة اللي رجعت:
function makeCounter() {
let count = 0; // متغيّر خاص، مخفي عن العالم الخارجي
return function () {
count += 1; // الدالة الداخلية بتعدّل count المحفوظ
return count;
};
}
const next = makeCounter();
console.log(next()); // 1
console.log(next()); // 2
console.log(next()); // 3
const other = makeCounter();
console.log(other()); // 1 — إغلاق مستقل بنسخته الخاصة من count
ركّز في آخر سطرين. other بدأ من 1 تاني، لأن كل استدعاء لـ makeCounter بيعمل بيئة جديدة ومتغيّر count جديد. الإغلاقات مش بتتشارك نفس المتغيّر إلا لو اتعرفوا في نفس النطاق. النقطة دي هي مفتاح الفخ الجاي.
الفخ الشهير: ليه الحلقة بتطبع نفس الرقم
الكود ده المفروض يطبع 0 و1 و2. بيطبع 3 و3 و3. السبب: var بيعمل متغيّر واحد على مستوى الدالة، وكل الـ callbacks بتقفل على نفس المتغيّر ده. لما الـ setTimeout يشتغل، الحلقة تكون خلصت و بقى 3.