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

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

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

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

المنصة

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

الدعم

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

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

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

Recursion للمبتدئ: ليه الدالة بتنده نفسها مش حلقة لا نهائية

📅 ٣٠ أبريل ٢٠٢٦⏱ 5 دقائق قراءة
Recursion للمبتدئ: ليه الدالة بتنده نفسها مش حلقة لا نهائية

مستوى المقال: مبتدئ

لو شفت دالة JavaScript بتنده نفس اسمها جوّاها، ده مش غلط ولا bug. ده Recursion. في 7 دقايق هتفهم ليه الكود ده مش بيدخل في حلقة لا نهائية، وامتى تستخدمه بدل الـ for loop بالظبط.

دمى ماتريوشكا روسية متداخلة كل واحدة جوا الأكبر منها لتمثيل فكرة الـ Recursion في البرمجة

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

أول مرة بتشوف دالة بتنده نفسها، الدماغ بترفض الفكرة. لأن لو الدالة بتنده نفسها، يبقى المفروض تفضل بتنده نفسها للأبد. الإجابة الصح: لأ، بشرط واحد بس. لو فهمت الشرط ده، Recursion هيبقى أبسط من الـ loops في حالات معينة — زي تصفّح مجلدات أو شجرة JSON أو DOM.

Recursion: لما الدالة تكون أبسط من الحلقة

مثال للمبتدئ — دمى ماتريوشكا

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

الـ recursion بنفس المنطق. الدالة بتعمل خطوة بسيطة على المدخل، وبعدين بتنده نفسها على نسخة أصغر من نفس المشكلة. بتفضل بتنده نفسها لحد ما توصل لحالة بسيطة جدًا الإجابة فيها معروفة على طول. الحالة دي اسمها base case. لو نسيت تكتبها، آه — هيدخل في حلقة لا نهائية فعلًا، والـ Node هيرميلك RangeError: Maximum call stack size exceeded بعد ثانية.

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

Recursion هي تقنية تنفيذ بتنحلّ فيها مشكلة عبر استدعاء الدالة لنفسها على نسخة أصغر من نفس المشكلة. كل recursion صحيح بيلتزم بشرطين بدون استثناء:

  1. Base case: حالة بسيطة الدالة بترجع فيها قيمة فورًا بدون ما تنده نفسها.
  2. Recursive case: استدعاء للدالة على input أصغر بحيث إنه بيقترب من الـ base case في كل خطوة.

كل استدعاء للدالة بيتسجّل في حتة في الذاكرة اسمها call stack. كل إطار (frame) في الـ stack بيحتفظ بقيم المتغيرات بتاعت الاستدعاء ده لحد ما يخلّص. لما الـ base case يرد قيمة، الـ stack بيبدأ يفك من فوق لتحت، وكل استدعاء بياخد قيمة الاستدعاء اللي تحته ويكمّل حسابه.

أبسط مثال كود — factorial

factorial(5) معناها 5 × 4 × 3 × 2 × 1 = 120. كود JavaScript شغّال على Node 22:

JavaScript
function factorial(n) {
  // base case: لو وصلنا لـ 1 ارجع مباشرة
  if (n <= 1) return 1;

  // recursive case: ضرب n في factorial(n-1)
  return n * factorial(n - 1);
}

console.log(factorial(5));  // 120
console.log(factorial(10)); // 3628800

اللي بيحصل لما تنده factorial(5) بالتفاصيل:

  • factorial(5) محتاج factorial(4) عشان يكمّل، فبيتحط فوق الـ stack وبيستنى.
  • factorial(4) محتاج factorial(3) — نفس القصة.
  • factorial(3) محتاج factorial(2).
  • factorial(2) محتاج factorial(1).
  • factorial(1) بيرجّع 1 على طول. ده الـ base case.
  • الـ stack بيرجع لتحت بالترتيب: 1 ثم 1×2=2 ثم 2×3=6 ثم 6×4=24 ثم 24×5=120.
مجموعة كتب مكدّسة فوق بعضها لتمثيل الـ call stack وكل استدعاء جديد لدالة JavaScript يُضاف لقمة المكدس

مثال عملي تاني — تصفّح مجلدات

عايز تطبع كل الملفات جوّا مجلد، حتى لو فيه مجلدات فرعية متداخلة 10 مستويات. مع loop عادي هتحتاج تتبع المستويات يدويًا بـ stack أو queue. مع recursion، الكود في 9 سطور:

JavaScript
import { readdirSync, statSync } from "node:fs";
import { join } from "node:path";

function listFiles(dir) {
  for (const name of readdirSync(dir)) {
    const fullPath = join(dir, name);
    if (statSync(fullPath).isDirectory()) {
      listFiles(fullPath); // الدالة بتنده نفسها على المجلد الفرعي
    } else {
      console.log(fullPath);
    }
  }
}

listFiles("./project");

لاحظ هنا: مفيش if صريح للـ base case. الـ base case ضمني — لما المجلد ميكونش جوّاه أي مجلدات فرعية، الـ for ميدخلش في الفرع التاني، والدالة بترجع طبيعي. ده pattern شائع في الكود الإنتاجي.

الفرق بين Recursion والـ Loop

أي recursion بتقدر تكتبه كـ loop، والعكس صحيح. السؤال: امتى يكون كل واحد فيهم أنضف؟

  • Recursion أحسن لما: البيانات شجرية أو متداخلة (مجلدات على القرص، DOM tree، JSON nested، graphs، شجرة تعليقات في تطبيق سوشيال).
  • Loop أحسن لما: البيانات خطّية وعدد التكرار معروف أو محدود (array بسيط، عدّاد، iteration على كل مستخدم في DB).

الـ trade-off هنا: كل استدعاء recursive بياكل من الـ call stack. في Node.js (V8) الحد الافتراضي حوالي 10,000 إطار قبل ما تشوف RangeError. الـ for loop ما عندوش الحد ده — بيعدّ على array من 10 مليون عنصر بدون مشكلة. كمان كل استدعاء فيه overhead بسيط لإنشاء الـ frame، فلو الـ hot path بتاعك بيتنفّذ مليون مرة في الثانية، الفرق يبقى محسوس.

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

  • لو العمق ممكن يعدّي 5,000 استدعاء وانت في بيئة JavaScript — لأن V8 لسه مفيهوش tail call optimization حقيقي رغم إن الـ ES2015 spec بيدعمها.
  • لو المشكلة خطّية بحتة (مجموع array من 100 مليون رقم — استخدم loop عادي).
  • في hot paths بتنفّذ مليار استدعاء/ثانية — تكلفة الـ stack frame بتظهر في الـ profiler.
  • لو الـ input جاي من المستخدم وممكن يكون عميق — مهاجم ممكن يبعت JSON عمقه 100,000 ويضرب السيرفر بـ stack overflow.

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

افتح محرر الكود واكتب دالة countDown(n) بتطبع الأرقام من n لحد 1 باستخدام recursion، بدون أي loop. لو نسيت الـ base case، Node هيقولك Maximum call stack size exceeded بعد أقل من ثانية. التمرين ده 3 دقايق وهيخلّي المفهوم يلصق فعلًا — لأن أحسن طريقة تفهم recursion إنك تعمل واحد بنفسك وتشوف ايه اللي بيكسر لما الـ base case غايب.

مصادر

  • MDN Web Docs — JavaScript Functions and Recursion: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions
  • V8 Blog — Proper Tail Calls in JavaScript: v8.dev/blog/modern-javascript
  • Node.js docs — V8 stack size and --stack-size flag: nodejs.org/api/cli.html
  • ECMAScript Language Specification — Function Calls and Execution Contexts: tc39.es/ecma262

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

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

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