المرحلة 0 · تقفيلة الأسبوع 1 · الدرس 0004 · ~30 دقيقة

المشهد الكامل — الـ Call Stack والـ Scopes شغالين مع بعض

الدرس ده اتولد من اقتراحك أنت: "نحط المحاكيين جنب بعض". هدفه الوحيد: تفصل الهيكلين عن بعض في دماغك للأبد — ويجهزك لأهم لحظة في المرحلة كلها (الـ Closures، أسبوع 3 إن شاء الله).

🔁 قبل ما تبدأ: الدرس ده تركيب مش تعلم جديد — لازم تكون خلصت درس 0003 الأول. لو خلصته، ادخل على طول.

١ — القاعدة اللي الدرس كله بيثبتها

عندك هيكلين منفصلين شغالين في نفس اللحظة، وكل واحد بيتحرك بقاعدة مختلفة:

🟦 الـ Call Stack🟥 الـ Scopes
بيتبني بإيه؟ترتيب النداء (runtime)ترتيب الكتابة في الملف (lexical)
مين بيضيف فيه؟استدعاء دالة فقطدالة أو بلوك { }
بيجاوب على؟"مين شغال دلوقتي ومين مستني؟""مين شايف أنهي متغير؟"

كلمة lexical يعني "حسب مكان الكتابة في الكود" — خليها في بالك، هي دي الكلمة اللي هتقابلك في تعريف الـ closures.

٢ — المحاكي المزدوج

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

💡 فين المتغيرات اللي كانت بتظهر جوه الـ frames في محاكي 0001؟ (سؤال سأله صاحب الورشة 😉) — موجودة، بس اترسمت مرة واحدة في لوحة الـ Scopes 🟥 بدل التكرار. الصورة الأدق: كل frame في الـ stack ماسك مؤشر لصندوق الـ scope بتاعه في لوحة النطاقات 🟥 — frame بتاع greetUser() بيشاور على صندوق "greetUser scope". محاكي 0001 رسمهم مدموجين (تبسيط)، وهنا مفصولين متوصلين (الصورة الأنضج). الـ stack = مين شغال، النطاقات = الذاكرة فين.

const app = "تطبيقي";
function greetUser() {
  const user = "عمر";
  if (true) {
    const emoji = "👋";
    log(user + emoji);
  }
}
function log(text) {
  console.log(text, app);
}
greetUser();
دوس «خطوة» عشان نبدأ.
Console:
🟦 Call Stack
(بيتبني بالنداء)
🟥 Scopes
(بيتبنوا بمكان الكتابة)

٣ — لحظتين المفاجأة (بعد ما تخلص المحاكي ارجع اقراهم)

🎯 المفاجأة الأولى (خطوة الـ if): البلوك فتح scope جديد في لوحة النطاقات 🟥... ولوحة الـ stack 🟦 ماتحركتش خالص. البلوكات بتعمل نطاقات، لكن مابتعملش stack frames — الـ stack للنداءات بس.

🎯 المفاجأة التانية (خطوة نداء log): الـ stack رصّها: Global ← greetUser ← log (ترتيب النداء). لكن بص فين فتح scope بتاع log في لوحة النطاقات 🟥: تحت الـ Global مباشرة — مش جوه greetUser ولا جوه البلوك! ليه؟ لأن log مكتوبة في مستوى الملف، فسلسلة بحثها بتطلع للـ Global على طول. علشان كده لما دورت على app لقتها، لكن لو دورت على user كانت هتضرب ReferenceError — رغم إن greetUser موجودة في الـ stack تحتيها في نفس اللحظة!

الخلاصة بجملة واحدة تتحفظ: الـ stack بيتبني بترتيب النداء، والـ scope chain بتتبني بترتيب الكتابة — ولحظة الـ lookup، المحرك بيمشي في سلسلة الكتابة، مش في الـ Call Stack.

✓ ناتج الكود (عمر👋 تطبيقي) متحقق منه بتشغيل Node.js فعلي (2026-08-04).

٤ — اختبر الفصل في دماغك

س1: دخلنا بلوك if { } جواه متغيرات — إيه اللي بيحصل للهيكلين؟

س2: دالة متكتبة في مستوى الملف واتندهت من جوه 3 دوال متداخلة — سلسلة بحثها بتطلع على مين؟

س3: إيه الجملة الصح؟

٥ — البذرة الأخيرة قبل الأسبوع الجاي

دلوقتي عندك السؤال الأعمق في JS، وسيبه يقلقك لحد الأسبوع 3 إن شاء الله 😉: شفت في المحاكي إن الدالة لما بتخلص، الـ frame بتاعها بيموت والـ scope بتاعها بيموت معاه... طب لو فيه طريقة تخلي دالة تخلص من الـ stack وscope بتاعها يفضل عايش حد شايله؟ دي مش لو — دي موجودة، واسمها Closure، وهي أقوى فكرة في اللغة كلها.

٦ — المصدر الأساسي