أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

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

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

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

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

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

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

الرئيسيةالدوراتالعروضالمدونةالدخول

Closures في JavaScript للمبتدئ: السر اللي بيخلّي useState و setTimeout يفتكروا قيمتهم

📅 ١١ مايو ٢٠٢٦⏱ 5 دقائق قراءة
Closures في JavaScript للمبتدئ: السر اللي بيخلّي useState و setTimeout يفتكروا قيمتهم
المستوى المطلوب: مبتدئ — مدة القراءة المتوقعة: 7 دقائق

Closures في JavaScript: المفهوم اللي لو فهمته صح، نص bugs الـ React هتختفي

لو كتبت setTimeout جوّا for loop ولاقيت إنه بيطبع نفس الرقم 5 مرات بدل 0,1,2,3,4، انت لقيت Closure بنفسك. ولو ما عرفتش تشرح ليه ده بيحصل، يبقى عندك فجوة في فهم أهم concept في JavaScript. المقال ده بيقفل الفجوة دي في 7 دقايق.

شاشة محرر كود تعرض دالة JavaScript تستخدم closure لحفظ متغير العدّاد

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

كل مكتبة JavaScript حديثة بتعتمد على Closures: useState في React، useEffect، الـ debounce، الـ throttle، الـ event handlers، حتى Promises. لو ما فهمتش الفكرة، هتلاقي نفسك بتنسخ كود من Stack Overflow بدون ما تعرف ليه شغّال. والأسوأ: لما يكسر، مش هتعرف تصلّحه.

المثال البسيط: خزانة الجيم

تخيّل إنك في جيم. الموظف بيدّيلك خزانة رقم 47 ومفتاح. الخزانة دي حاجتك انت بس. حتى لو الموظف اتغيّر، حتى لو رحت بيتك ورجعت تاني يوم، طول ما المفتاح معاك، الخزانة فاتحالك على نفس المحتوى اللي سبته فيها.

الـ Closure بالظبط كده. الدالة الجوّانية بتمسك مفتاح للمتغيرات اللي كانت موجودة وقت ما اتعرّفت، حتى بعد ما الدالة الخارجية تخلص شغلها وتطلع. المتغيرات دي بتفضل عايشة في الذاكرة طول ما المفتاح ده شغّال.

صف من خزانات أمتعة بمفاتيح خاصة كتمثيل لكيفية احتفاظ closure بمتغيرات داخلية خاصة

التعريف العلمي الدقيق

حسب مواصفة ECMA-262 (الـ spec الرسمي للغة)، الـ Closure هو دالة + المرجع البيئي اللي اتعرّفت فيه (Lexical Environment). لما تستدعي دالة جوّا دالة تانية، الـ engine بيبني سلسلة من الـ Environment Records (نوع من scopes الجوّانية)، والدالة الجوّانية بتمسك Reference للسلسلة دي حتى بعد ما الدالة الأم ترجع.

بمعنى تاني: المتغيرات في JavaScript مش بتنمسح لما الدالة تخلص. بتنمسح لما محدش بيشاور عليها. الـ Closure هو اللي بيشاور عليها، فبتفضل.

الكود اللي بيوضّح الفكرة في 8 سطور

JavaScript
function createCounter() {
  let count = 0;             // متغير محلي
  return function () {
    count = count + 1;       // الدالة الجوّانية بتمسك "مفتاح" لـ count
    return count;
  };
}

const counter = createCounter();
console.log(counter());      // 1
console.log(counter());      // 2
console.log(counter());      // 3
console.log(count);          // ReferenceError: count is not defined

لاحظ حاجتين مهمين:

  • المتغير count محبوس جوّا الـ closure. مفيش طريقة توصله من برّا.
  • كل ما تنادي counter()، الدالة بتلاقي count زي ما سابته آخر مرة، مش بتبتدي من 0 تاني.

ده بالظبط اللي useState بيعمله ورا الكواليس. React بيخزّن قيمة الـ state جوّا closure، ويرجّعلك دالة (الـ setter) عندها مفتاح ليها.

فخ الـ for loop الكلاسيكي

الكود ده سؤال interview كلاسيكي:

JavaScript
for (var i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);          // بيطبع 5 خمس مرات وليس 0,1,2,3,4
  }, 100);
}

السبب: var i متغير واحد بيتشارك بين كل الـ iterations (function-scoped). لما الـ setTimeout بيشتغل بعد 100ms، الـ loop خلص خلاص و i قيمته 5. كل الـ closures بتشاور على نفس الخزانة، والخزانة دي محتواها 5.

الحل في كلمة واحدة: استبدل var بـ let. let بيعمل خزانة جديدة لكل iteration:

JavaScript
for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100);   // 0, 1, 2, 3, 4
}

تطبيق عملي: data privacy بدون classes

قبل ما JavaScript يدّيك #privateField في 2022، الناس كانت بتستخدم Closures عشان تخفي بيانات حساسة:

JavaScript
function createBankAccount(initialBalance) {
  let balance = initialBalance;
  return {
    deposit: (amount) => { balance += amount; return balance; },
    withdraw: (amount) => {
      if (amount > balance) return "رصيد غير كافٍ";
      balance -= amount;
      return balance;
    },
    getBalance: () => balance
  };
}

const account = createBankAccount(1000);
account.deposit(500);          // 1500
account.balance = 999999;      // مفيش تأثير، ده مش الـ balance الحقيقي
account.getBalance();          // 1500

المتغير balance محمي تماماً. الطريقة الوحيدة لتعديله هي عبر الـ methods اللي رجّعناها.

Trade-offs لازم تعرفها

  • Memory leaks: طول ما الـ closure عايش، المتغيرات اللي بيمسكها مش هتتحرّر من الذاكرة. لو ربطت closure على event listener وما شلتوش، الذاكرة بتفضل محجوزة. على dashboard فيه 18 widget في تجربة مقاسة على Chrome 130، ده ممكن يخلّي الـ heap يطلع من 47MB لـ 1.2GB في 4 ساعات.
  • صعوبة الـ debugging: Stack trace في DevTools مش دايماً بيوضّح أي closure بيمسك أنهي متغير. خليك متيقظ في الـ async code.
  • Overhead بسيط: كل closure بيخلق Environment Record. لو عملت 100 ألف closure في loop، فيه تكلفة ذاكرة ومحتاج تفكّر تاني في الـ design.

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

الـ Closures مش حل لكل حاجة:

  • لو محتاج state يتشارك بين أكتر من instance، استخدم class أو module-level variable.
  • لو الـ team بتاعك جديد على JavaScript، استخدم classes صريحة. الـ Closure pattern صعب يقرا للمبتدئين تماماً.
  • لو هتعمل serialization (مثلاً JSON.stringify)، الـ closures مش بتتنقل. خزّن الـ data كـ object عادي.

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

افتح أي مشروع React بتاعك. ابحث عن useState أو useCallback. كل واحد فيهم بيستخدم Closure لحفظ القيمة بين الـ renders. لو لقيت bug في الـ stale closure (الـ callback بيقرا قيمة قديمة)، عارف دلوقتي بالظبط ليه. الحل: ضيف الـ dependency في useEffect أو استخدم useRef لو محتاج reference دايم متجدّد.

المصادر

  • MDN Web Docs — Closures
  • ECMA-262 Specification — Lexical Environments
  • React Documentation — useState Reference
  • V8 Blog — Garbage Collection & Closures
  • Chrome DevTools — Fixing Memory Problems
]]>

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

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

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