أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالمناهج والباقات
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • المناهج والباقات
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالمناهجالمدونةالدخول
البرمجة بالعربي

Closures في JavaScript للمبتدئ: ازاي الدالة بتفتكر متغيّر بعد ما تخلص

مبتدئ١٤ يونيو ٢٠٢٦5 دقائق قراءة
Closures في JavaScript للمبتدئ: ازاي الدالة بتفتكر متغيّر بعد ما تخلص

مستوى المقال: مبتدئ. لو لسه ماسك JavaScript من شوية وسمعت كلمة Closure وحسّيت إنها سحر، المقال ده هيخليها أوضح حاجة عندك في اللغة.

لو دالة في JavaScript رجّعتلك رقم بيزيد كل مرة تناديها — رغم إن الدالة الأصلية خلصت وانتهت — انت بالظبط قدام الـ Closure. ده مش bug ولا حاجة غريبة. ده أهم مفهوم في اللغة، ولو فهمته هتكتب كود أنضف وتبطّل تخاف من الأخطاء اللي ملهاش معنى.

Closures في JavaScript: الدالة اللي بتفتكر

شاشة محرر كود تعرض دالة JavaScript بداخلها دالة أخرى لتوضيح فكرة الـ Closure

المشكلة باختصار

المتغيّر اللي بتعرّفه جوه دالة المفروض يموت لما الدالة تخلّص شغلها. ده السلوك الطبيعي. لكن في 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 المتصفح مباشرة:

JavaScript
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

لاحظ حاجة غريبة: createCounter() اتنفّذت مرة واحدة بس وخلصت. المفروض count يموت معاها. بس كل مرة تنادي next() الرقم بيزيد. ده معناه إن count لسه عايش في الذاكرة، محفوظ جوه الـ Closure. وأهم نقطة: لو عملت const next2 = createCounter() هتلاقي next2 ليه عدّاد منفصل تماماً يبدأ من 1 — زي خزانة جديدة بمفتاح جديد.

مطوّر يكتب كود JavaScript على شاشتين لتتبّع المتغيرات داخل نطاق الدالة

الخطأ الأشهر للمبتدئين: الـ for loop

الكود ده بيوقّع 90% من اللي بيتعلموا. خمّن المخرجات قبل ما تكمّل:

JavaScript
var funcs = [];
for (var i = 0; i < 3; i++) {
  funcs.push(function () {
    return i;
  });
}
console.log(funcs[0](), funcs[1](), funcs[2]());
// المتوقع: 0 1 2 — الحقيقي: 3 3 3

ليه طلعت 3 3 3؟ لإن var بيعمل متغيّر واحد مشترك على مستوى الدالة كلها. التلات دوال ماسكين نفس الخزانة، ولما الـ loop خلص قيمة i بقت 3. فكلهم بيقروا 3.

الحل في سطر واحد: غيّر var لـ let. الـ let بيعمل متغيّر جديد لكل لفّة في الـ loop، فكل دالة بتاخد خزانتها الخاصة:

JavaScript
for (let i = 0; i < 3; i++) {
  funcs.push(() => i);
}
console.log(funcs[0](), funcs[1](), funcs[2]()); // 0 1 2

الفرق ده مش تفصيلة. ده سبب رئيسي إن let اتضاف أصلاً في ES2015 (ECMAScript 6). الافتراض هنا إنك بتكتب كود حديث؛ لو لسه شايف var في كود قديم، ده أول مكان تشك فيه لما تلاقي قيم غريبة.

سيناريو واقعي ورقم

لو عندك تطبيق فيه 1000 زرار، وكل زرار محتاج يفتكر بياناته الخاصة (id مثلاً)، الـ Closure بيوفّر عليك إنك تخزّن الـ id في الـ DOM وترجع تقراه كل مرة. القراءة من DOM بتاخد حوالي 0.1ms لكل عملية؛ القراءة من Closure في الذاكرة بتاخد أقل من 0.001ms — أسرع حوالي 100 مرة في الحالة دي. المكسب مش ضخم لزرار واحد، بس لو بتعالج آلاف الـ handlers في الثانية بيفرق فعلاً.

الـ trade-offs اللي لازم تعرفها

  • الذاكرة: المتغيّر اللي ماسكه الـ Closure مش بيتمسح بالـ Garbage Collector. بتكسب إنه بيفضل متاح، بتخسر إنه بياخد مكان في الرام طول ما الدالة عايشة.
  • الوضوح: الـ Closures بتخفي الـ state. بتكسب تغليف (encapsulation) نظيف، بتخسر إن debugging بيبقى أصعب لإن القيمة مش ظاهرة في الكود مباشرة.
  • التتبّع: لو عندك Closures متداخلة كتير، فهم مين ماسك مين بيبقى متعب. بتكسب قوة، بتخسر بساطة القراءة.
  • الأداء: إنشاء Closure أغلى شوية من دالة عادية، بس الفرق مهمل (ميكروثواني) إلا لو بتعمل ملايين منهم في loop ضيق.

متى لا تستخدم Closures

لو محتاج تشارك state بين أجزاء كتير من البرنامج، الـ Closure غلط — ده شغل module أو class أو state management مركزي. كمان لو بتعمل Closures جوه loop بيلفّ ملايين المرات وبتخزّن فيها بيانات كبيرة، ممكن تعمل تسريب ذاكرة (Memory Leak): المتغيّرات بتتراكم ومتتمسحش، والرام بتطلع لفوق لحد ما السيرفر يقع. في الحالة دي صفّر المراجع يدوياً بـ ref = null بعد ما تخلص.

الخطوة التالية

افتح console المتصفح دلوقتي والصق دالة createCounter اللي فوق. ناديها مرتين في متغيّرين مختلفين، وشوف بنفسك إزاي كل عدّاد مستقل. لو القيم اتخلطت مع بعض، يبقى عندك var مكان let في مكان ما — دوّر عليها.

المصادر

  • تعريف الـ Closure والبيئة المعجمية: توثيق MDN Web Docs من Mozilla — "Closures".
  • مفهوم Lexical Scoping و let/const: مواصفة ECMAScript 2015 (ES6) الرسمية من TC39.
  • سلوك var مقابل let في الحلقات: توثيق MDN — "let" و"var".

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة