المستوى المطلوب: متوسط — المقال ده مكتوب لمطوّر JavaScript شغّال بالفعل وعارف الـ functions والـ variable scope، بس مش فاهم بالظبط ليه الـ Closure مهم، ولا فين بيظهر في الكود اللي بيكتبه كل يوم.
لو كتبت setTimeout وبتستغرب إزاي بتفتكر المتغيرات اللي حواليها بعد ما الدالة الأصلية انتهت، أو لو شاركت في code review واتقالك "في stale closure هنا"، انت بتشتغل مع Closures من غير ما تعرفها. المقال ده هيخلّيك تفهمها بدقة، تعرف فين بالظبط في كودك بتأثّر، وإمتى الـ Closure نفسه يبقى مشكلة مش حل.
Closures في JavaScript — المفهوم اللي بيفرق بين junior و senior
في كل مقابلة JavaScript جدية، سؤال الـ Closure بيظهر — مش لأنه أكاديمي، لكن لأن 4 من أصل 5 أنماط بتستخدمها في React و Node.js مبنية فوقه. لو فهمته صح، هتكتب debounce في 5 سطور، وتحل bug "الـ counter مش بيتحدّث" في React في 30 ثانية، وتعرف ليه تطبيقك بيستهلك ذاكرة بدون سبب واضح.
المشكلة باختصار
JavaScript بيسمحلك ترجّع دالة من جوّه دالة تانية. الدالة المرجّعة بتفضل قادرة على قراءة وتعديل متغيرات الدالة الأم — حتى بعد ما الأم خلصت ورجعت قيمتها. ده اللي بنسمّيه Closure. المشكلة إن أغلب المطوّرين بيستخدموه يوميًا بدون ما يدركوا، فلما يحصل bug مرتبط بيه بياخدوا ساعات قبل ما يفهموا اللي حصل.
تخيّل معايا: الدفتر الشخصي للموظف
افترض إن محمد اشتغل في شركة سنتين، وفي يومه الأخير كتب في دفتره الشخصي أرقام التليفونات والإجراءات الداخلية اللي اتعلّمها. ساب الشركة وانضم لشركة تانية. الشركة الأولى قفلت بعد سنة — لكن محمد لسه عنده الدفتر. لو سألته عن إجراء قديم بعد سنين، هيرد فورًا من نفس الدفتر.
الـ Closure بيشتغل بالظبط كده. الدالة الجوّانية = محمد. الدفتر = المتغيرات اللي كانت موجودة لمّا الدالة الأم اتنفّذت. لما الأم "بتقفل" (بتنتهي وترجع)، الدفتر بيفضل مع الدالة الجوّانية ما دام في مرجع ليها في أي مكان من البرنامج.
تعريف Closure بدقة
الـ Closure في JavaScript هو تركيبة من دالة + الـ lexical environment اللي اتعرّفت فيه. الـ lexical environment ده هيكل بيانات داخلي بيحتفظ بمراجع لكل متغير محلي كان متاح وقت تعريف الدالة. لمّا تنشئ دالة جوّه دالة وترجّعها، الـ JavaScript engine بيربط الدالة الجوّانية بالـ environment بتاع الأم، فالـ Garbage Collector ميقدرش يحرّر متغيرات الأم طول ما الجوّانية لسه حيّة.
المرجع التقني الرسمي: ECMAScript 2024 Language Specification — قسم 9.4 "Execution Contexts" وقسم 10.2.1.1 "OrdinaryFunctionCreate". MDN Web Docs بتسمّيها "Closures" في Guide → Functions.
أبسط مثال شغّال — counter من غير class
function createCounter() {
let count = 0;
return {
increment() { count += 1; return count; },
decrement() { count -= 1; return count; },
value() { return count; }
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1
console.log(counter.value()); // 1
console.log(counter.count); // undefined — مش متاح من بره