المستوى: مبتدئ — مش محتاج خبرة سابقة، بس تكون كتبت دالة (function) مرة قبل كده.
الإغلاقات (Closures): ليه الدالة تفضل فاكرة متغيّراتها بعد ما تخلص؟
لو كتبت دالة جوّه دالة تانية ولقيت الدالة الجوّانية لسه فاكرة متغيّر خلص عمره من زمان، ده مش عطل. ده الـ Closure. وهو الحاجة اللي بيقوم عليها نص كود جافاسكريبت الحديث: العدّادات، الـ private state، الـ event handlers، والـ debounce. تفهمه صح دلوقتي، هتبطّل تتلغبط في أخطاء غريبة بعد كده.
المشكلة باختصار
القاعدة اللي حافظها إن المتغيّر المحلي بيموت لما الدالة تخلص. طيب إزاي دالة جوّانية بترجع رقم بيزيد كل مرة، رغم إن الدالة الأم اشتغلت مرة واحدة وخلصت؟ ده اللي بيلخبط ناس كتير، وهو بالظبط شغل الـ Closure.
الفكرة بمثال قبل أي كود
خُد المثال ده الأول عشان تحس بالفكرة. موظف في مكتبة ماشي وهو شايل شنطة صغيرة، جوّاها ورقة مكتوب عليها رقم. كل ما حد ييجي يسأله، بيطلع الورقة، يزوّد الرقم واحد، ويرجّعها الشنطة. المكتبة ممكن تقفل، المدير ممكن يمشي، بس الموظف لسه ماسك شنطته والرقم اللي جوّاها ماحدش لمسه.
الدالة الجوّانية في جافاسكريبت بتعمل بالظبط كده. لما بتترجع من دالة أم، بتمشي وهي شايلة شنطة فيها المتغيّرات اللي اتولدت حواليها. الدالة الأم تخلص وتتمسح من الذاكرة، بس الشنطة تفضل مع الدالة الجوّانية طول ما هي عايشة.
التعريف العلمي الدقيق
بالتحديد: الـ Closure هو دالة متجمّعة مع البيئة المعجمية (lexical environment) اللي اتعرّفت جوّاها. النقطة المهمة إن الدالة بتمسك مرجع (reference) للـ scope بتاعها، مش نسخة من القيم. عشان كده بتشوف آخر قيمة للمتغيّر، ولو المتغيّر اتغيّر بعدين، الدالة بتشوف التغيير. الكلمة معجمي معناها إن الربط بيتحدّد من مكان كتابة الكود في الملف، مش من مكان استدعائه.
كود شغّال في 12 سطر
function makeCounter() {
let count = 0; // متغيّر محلي جوّه الدالة الأم
return function () { // الدالة الجوّانية = الـ closure
count += 1; // بتوصل لـ count رغم إن makeCounter خلصت
return count;
};
}
const next = makeCounter();
console.log(next()); // 1
console.log(next()); // 2
console.log(next()); // 3
ركز في اللي حصل: makeCounter اشتغلت مرة واحدة، رجّعت الدالة الجوّانية، وخلصت. المفروض count تتمسح دلوقتي. بس next بيفضل يزوّدها ويطبع 1 ثم 2 ثم 3. ده لإن next ماسك مرجع للـ scope اللي فيه count. كل نداء لـ makeCounter() بيعمل شنطة جديدة مستقلة تمامًا.
الفخّ الشائع اللي بيوقّع المبتدئين
المثال ده بيطلع نتيجة غلط بيتفاجئ بيها الناس. الافتراض إنه هيطبع 0 و1 و2: