الـ Callbacks من الصفر — الدوال لما تبقى قيم بتتبعت
الدرس ده اتولد بطلب منك: حسيت إن أساس الـ callbacks مهزوز فوقّفنا كل حاجة وبنبنيه صح. بعده هتفهم فخ الـ this فهم كامل، وهو نفسه أول حلقة في سلسلة الـ async (الأسبوع 6 إن شاء الله) — يعني استثمار مزدوج.
🔁 قبل ما تبدأ: الدرس ده مالوش prerequisites غير أساسيات الدوال والـ scope (خلصتهم كلهم ✓). ادخل على طول — وسيبك من أي حاجة قريتها عن callbacks قبل كده، هنبني من الصفر.
١ — الأساس اللي كل حاجة مبنية عليه: الدالة قيمة
في JS، الدالة قيمة زيها زي الرقم والنص بالظبط — تتخزن في متغير، تتحط في أوبجكت، وتتبعت كـ argument لدالة تانية. (الاسم العلمي: first-class functions — الدوال مواطنين درجة أولى.)
عشان كده الكود ده شغال عادي جداً:
function sayHi() { console.log("هاي"); }
const g = sayHi;
g(); // "هاي"
السطر التاني نسخ قيمة الدالة جوه المتغير g، والسطر التالت نده عليها من اسمها الجديد فطبعت "هاي" عادي.
٢ — أهم تفرقة في الدرس كله: f غير f()
| الكتابة | معناها | بتديك إيه |
|---|---|---|
sayHi (من غير أقواس) | "الدالة نفسها كقيمة" | مؤشر للدالة — ماتنفذتش |
sayHi() (بالأقواس) | "نفّذها دلوقتي حالاً" | ناتج تنفيذها |
الأقواس () هي أمر التشغيل. من غيرها أنت بتشاور على الدالة، بيها أنت بتشغّلها. والخلط بينهم أشهر باج مبتدئين في التاريخ:
setTimeout(sayHi, 1000); // ✓ setTimeout(sayHi(), 1000); // 💥 TypeError
السطر الأول بعت الدالة كقيمة، فهتتنفذ بعد ثانية زي ما متوقع. أما السطر التاني: الأقواس نفّذت sayHi فوراً في نفس اللحظة، وبالتالي setTimeout استلمت ناتج التنفيذ (اللي هو undefined) بدل ما تستلم الدالة — فرمت TypeError.
✓ الباج ده متحقق منه بتشغيل Node فعلي (2026-08-05): طبعت فوراً وبعدين انفجر البرنامج.
٣ — يبقى إيه الـ Callback بقى؟
① الـ Callback = دالة بتبعتها كقيمة لدالة تانية، عشان التانية دي تناديها هي في الوقت المناسب.
② الدالة المستقبِلة (اللي بتاخد دالة كـ argument) اسمها Higher-Order Function — "دالة من رتبة أعلى" لأنها بتتعامل مع دوال مش بس بيانات.
③ الاسم نفسه بيشرح: call back = "عاود الاتصال" — أنت بتسيب رقمك (الدالة) وبتقول "اتصل بيا لما يحصل كذا".
٤ — فك السحر: ابني higher-order function بإيدك
أكبر سبب للغموض إن الناس بتشوف callbacks جوه أدوات جاهزة (setTimeout, forEach) وفاكرة إن فيه سحر جواها. مفيش — المستقبِل بينده باراميتره وخلاص، شوف:
function repeat(n, action) {
for (let i = 0; i < n; i++) {
action(i);
}
}
function sayNum(i) { console.log("رقم " + i); }
repeat(3, sayNum);
اقرا repeat كإنك المحرك: action باراميتر عادي جداً استلم دالة كقيمة، والسطر action(i) بينده الباراميتر ده نداء عادي — لأنه أصلاً دالة. الناتج: "رقم 0" ثم "رقم 1" ثم "رقم 2". كل higher-order function في الدنيا — من setTimeout لـ forEach — جواها نفس الفكرة دي بالظبط. ✓ متحقق بالتشغيل
🌍 وده اللي بتستخدمه يومياً من غير ما تحس:
items.forEach(print); // ناديها على كل عنصر
button.addEventListener("click", save); // ناديها لما يدوس
setTimeout(remind, 1000); // ناديها بعد ثانية
كلهم نفس العقد: "خد الدالة دي، وناديها أنت لما الشرط بتاعك يتحقق". (تفاصيل إزاي setTimeout بتستنى من غير ما توقف البرنامج — دي حكاية الـ Event Loop في الأسبوع 6 إن شاء الله.)
٥ — شوفها بعينك: رحلة الـ callback جوه المحاكي
سيناريو A بيوريك الميكانيكا النضيفة، وسيناريو B بيوصّلها بفخ الـ this اللي قابلته في 0005. راقب باراميتر action جوه الـ frame — هتشوف الدالة واصلة كقيمة!
function repeat(n, action) { for (let i = 0; i < n; i++) { action(i); } } function sayNum(i) { console.log("رقم " + i); } repeat(2, sayNum);
٦ — توقع قبل ما تشوف (3 تمارين)
function greet() { console.log("أهلاً"); }
setTimeout(greet(), 500);
function twice(fn) { fn(); fn(); }
twice(function () { console.log("تم!"); });
function run(action) { action(); }
const user = { name: "عمر", hi() { console.log("أهلاً " + this.name); } };
run(user.hi);
٧ — اختبر نفسك
س1: إيه الفرق بين sayHi و sayHi() كـ argument؟
س2: يعني إيه higher-order function؟
س3: جوه repeat، السطر action(i) بيعمل إيه؟
س4 (الربط بدرس this): ليه run(user.hi) بتطبع undefined؟
٨ — التحدي الأخير: ابني بإيدك (من غير AI!)
المرة دي مش شرح — كتابة كود. افتح أي ملف أو console واكتب بنفسك (بالترتيب):
① عرّف دالة بسيطة اسمها sayHi بتطبع أي رسالة — دي هتبقى "فار التجارب" بتاعنا.
② اكتب دالة اسمها callSafely(fn): بتستقبل قيمة — لو اللي وصلها فعلاً دالة (typeof fn === "function") تناديها، ولو لأ تطبع "دي مش دالة!".
③ جرّبها بالتلات حالات دول وتوقع الناتج قبل كل واحدة: callSafely(sayHi) ثم callSafely(sayHi()) ثم callSafely("نص").
ابعتلي الكود اللي كتبته ونتايج التجارب التلاتة — ودي هتبقى أول مهمة كتابة كود بينا، على قد ما هي بسيطة على قد ما بتثبت الفكرة كلها.
٩ — المصدر الأساسي للتعمق
- MDN — Callback function — التعريف المرجعي.
- MDN — First-class Function — الدوال كقيم.
- javascript.info — Introduction: callbacks — اقرا أول قسمين بس، ووقّف عند "callback in callback" (الباقي بيدخل في async بتاع الأسبوع 6 إن شاء الله).