📌 مرجع سريع: نموذج التنفيذ في JavaScript
وحدة معرفة مضغوطة — بتتحدث مع كل درس في المرحلة 0. آخر تحديث: درس 0011 (Currying).
المصطلحات (Glossary)
| المصطلح | التعريف في سطر |
|---|---|
| Execution Context | بيئة تنفيذ بتتعمل لكل استدعاء function (+ واحدة Global) — فيها المتغيرات والـ scope وقيمة this |
| Call Stack | الـ contexts النشطة مترصّين فوق بعض — LIFO (Last In, First Out: آخر داخل أول خارج): اللي فوق هو اللي شغال دلوقتي |
| Creation Phase | قبل التنفيذ: حجز المتغيرات والدوال في الذاكرة (أصل الـ hoisting) |
| Execution Phase | تنفيذ الكود سطر سطر وإسناد القيم |
| Stack Frame | العنصر الواحد جوه الـ stack (بيمثل execution context واحد) |
| Stack Overflow | RangeError: Maximum call stack size exceeded — تراكم frames من غير خروج (غالباً recursion بلا توقف) |
القواعد الذهبية
- الـ context بيتعمل عند الاستدعاء — مش عند التعريف.
- كل context بيمر بمرحلتين: إنشاء ثم تنفيذ.
- function خلصت (return أو آخر سطر) = context خرج من الـ stack فوراً.
- JS بمحرك واحد synchronous: context واحد بس بيتنفذ في أي لحظة (اللي فوق). — الـ async بييجي في الأسبوع 6 إن شاء الله.
التتبع بالإيد (Hand Tracing) — أنت المحاكي
نفس محاكي درس 0001 بس بورقة وقلم: بتقرا الكود سطر سطر وبتكتب كل حدث (دالة دخلت/خرجت) وشكل الـ stack بعده. بتستخدمها لما تقابل كود مش فاهمه — في انترفيو أو debugging — ومفيش محاكي جاهز:
| الحدث (بترتيب الحصول) | الـ stack بعده |
|---|---|
greet("عمر") اتنادت ← push | Global → greet |
جواها: build("عمر") اتنادت ← push | Global → greet → build |
build عملت return ← pop | Global → greet |
console.log(msg) ← push ثم pop بسرعة | Global → greet |
greet خلصت ← pop | Global |
القاعدة: كل نداء اكتبه بإزاحة لجوه (عمق أكتر في الـ Call Stack)، وكل return ارجع بره. لما حد يقولك "اشرح الكود ده" — ده أول قلم تحطه على الورق.
Hoisting والـ TDZ (من درس 0002)
var | let / const | function declaration | |
|---|---|---|---|
| قيمتها في مرحلة الإنشاء | undefined | ⏳ من غير قيمة (TDZ) | الدالة كاملة ✓ |
| الوصول قبل سطرها | undefined (صامت) | 💥 ReferenceError | بتشتغل عادي |
- Hoisting = تسجيل التعريفات في الـ context أثناء مرحلة الإنشاء قبل تنفيذ أي سطر — مش "تحريك كود لفوق".
- TDZ = Temporal Dead Zone (المنطقة الميتة المؤقتة): من بداية الـ scope لحد سطر تعريف let/const — المتغير محجوز وممنوع لمسه.
- الفخ الشهير:
foo()قبلvar foo = function(){}←TypeError(مش ReferenceError) لأن foo موجودة بقيمة undefined. - القاعدة العملية:
constافتراضياً ←letعند إعادة الإسناد ←varمالهاش مكان في كود جديد.
✓ كل السلوكيات دي متحقق منها بتشغيل Node.js فعلي (2026-08-03).
Scope وسلسلة البحث (من درس 0003)
- 3 أنواع نطاقات: Global (الملف) / Function (الدالة) / Block (أي
{ }— للـ let/const بس). - Scope Chain: البحث بيمشي من جوه لبره فقط (النطاق الحالي ← الأب ← الـ Global). الخارجي عمره ما يشوف الداخلي. مش لاقي الاسم في السلسلة كلها =
ReferenceError. - فخ var: بتتجاهل البلوكات تماماً (
if { var y = 1 }← y متسربة بره!) وبتحترم الدوال بس. - Shadowing: اسم داخلي بيغطي على الخارجي جوه نطاقه — لأن البحث بيقف عند أول لقية.
- قاعدة الكود النضيف: عرّف المتغير في أضيق نطاق ممكن — أقل عيون شايفاه = أقل باجات.
✓ متحقق من كل السلوكيات بتشغيل Node.js فعلي (2026-08-04).
قواعد الـ this الأربعة (من درس 0005)
| الأولوية | شكل النداء | this بيبقى |
|---|---|---|
| 1 — new | new Person() | الأوبجكت الجديد اللي بيتبني |
| 2 — explicit | f.call(obj) / apply / bind | الـ obj اللي حددته بإيدك |
| 3 — implicit | obj.f() | اللي قبل النقطة |
| 4 — default | f() | globalThis (undefined في strict) |
- القانون الحاكم: this بيتحدد من شكل سطر النداء لحظة التنفيذ — مش مكان الكتابة. (عكس الـ scope بالظبط: scope بالكتابة، this بالنداء.)
- الفخ الأشهر:
const f = obj.m; f()← النقطة مش بتتخزن مع الدالة ← default binding ← this ضاع. - الـ arrow functions (درس 0008): مالهاش this خاص — بتورثه من مكان كتابتها عبر سلسلة البحث (lexical). call/apply/bind بيتتجاهلوا تماماً، وnew بترمي TypeError. القاعدة العملية: methods بدوال عادية، callbacks داخلية بـ arrows.
- call/apply/bind (درس 0007):
call(obj, a, b)فوري بفواصل /apply(obj, [a,b])فوري بـ array (Call=Commas, Apply=Array) /bind(obj)مش بينادي — بيرجع دالة جديدة مقفولة الـ this (hard binding: النداءات اللاحقة حتى call مابتكسرش القفل — الاستثناء الوحيد اللي بيكسبها هوnew، لأنها أولوية 1 في الجدول فوق ضد أولوية 2 بتاعة bind؛ المثال الكامل المتحقق منه في قسم ٥ من درس 0007). حل فخ الـ callback:setTimeout(user.hi.bind(user), 1000).
✓ متحقق من القواعد الأربعة وأولويتهم بتشغيل Node.js فعلي (2026-08-04).
Closures (من درس 0010)
- closure = دالة + رابط دايم بمتغيرات الـ scope اللي اتعرّفت جواه (سلسلة البحث، 0003) — مش ميزة خاصة، نتيجة طبيعية لإزاي JS بيتعامل مع الدوال.
- لما دالة خارجية تخلص ويتشال الـ frame بتاعها من الـ Call Stack، متغيراتها مش بتتمسح طالما لسه فيه دالة داخلية شايلة رابط ليها — البيئة بتفضل حية بره الـ stack.
- كل نداء منفصل لدالة factory (زي
makeCounter()) بيرجع closure مستقل تمامًا — بيئة جديدة كل مرة، مش بيئة مشتركة. - الـ closure شايف نفس المتغير الحي، مش نسخة من قيمته وقت التعريف — لو المتغير اتغيّر بعد التعريف وقبل النداء، الـ closure هيشوف القيمة الجديدة.
- الاستخدام العملي الأشهر: private state — تخبي متغير بحيث الوصول الوحيد ليه يكون عن طريق دوال محددة بترجعها.
✓ متحقق من كل السلوكيات بتشغيل Node.js فعلي (2026-08-14). الدرس المرتبط: 0010 — Closures.
Currying (من درس 0011)
- Currying = تحويل دالة بتاخد كذا argument لسلسلة دوال، كل واحدة بتاخد argument واحد بس وبترجع الدالة اللي بعدها —
f(a)(b)(c). الاسم منسوب لعالم المنطق Haskell Curry، مش وصف حرفي. - الآلية بالكامل closures — نفس مبدأ 0010: كل مستوى شايل 🎒 رابط لمتغيرات المستوى اللي قبله، وكل الحقائب دي مرتبطة في سلسلة لحد آخر نداء ينفّذ الحساب.
- الفرق عن partial application بـ bind (0007): bind بتاخد كل الباقي دفعة واحدة (
f.bind(null, a); g(b, c))، أما currying فكل مستوى argument واحد فقط دايمًا —add(1)(2, 3)بترجع دالة مش رقم، لأن الـ 3 بيتجاهله المستوى الحالي تمامًا. - كل نداء منفصل للدالة الأصلية (
add(1)ضدadd(2)) بيرجع سلسلة حقائب مستقلة تمامًا — نفس مبدأ استقلالية الـ closures من 0010.
✓ متحقق من كل السلوكيات بتشغيل Node.js فعلي (2026-08-14). الدرس المرتبط: 0011 — Currying.