Debugging في JavaScript: دليل اكتشاف الأخطاء وإصلاحها
من Console وStack Trace إلى Breakpoints وNetwork: منهج عملي لتحديد مكان الخطأ وسببه ثم اختبار الإصلاح بدل الاعتماد على التخمين.
ما هو Debugging في JavaScript؟
يقصد بـDebugging تتبع السلوك غير المتوقع في البرنامج حتى تصل إلى سببه، ثم إصلاحه واختبار النتيجة. لا يقتصر الأمر على التخلص من رسالة الخطأ؛ المطلوب معرفة موضع الخلل وكيف أدى إلى المشكلة التي ظهرت.
ابدأ بإعادة إنتاج المشكلة
قبل أن تغيّر أي سطر، حاول إعادة المشكلة بخطوات واضحة. سجّل الصفحة أو المسار المعني، والمدخلات التي استخدمتها، وما كنت تتوقع حدوثه مقارنة بما حدث فعلًا. إذا تعذر تكرار الخطأ، اجمع أدلة إضافية بدل إجراء تعديلات عشوائية.
اقرأ Console وStack Trace
غالبًا ما يوضح Console فئة الخطأ ويشير إلى موضع محتمل له. افحص اسم الملف ورقم السطر وتسلسل الاستدعاءات في Stack Trace، ثم ركّز على أول جزء من التتبع يعود إلى كود مشروعك بدل الانشغال بإطارات المكتبات الخارجية.
const user = null;
console.log(user.name); // TypeErrorفي هذا المثال لا يكفي حذف السطر؛ اسأل لماذا كانت قيمة user تساوي null في هذه النقطة.
استخدم Breakpoints بدل console.log فقط
ضع Breakpoint قبل السطر الذي تظهر عنده المشكلة، ثم نفذ الكود خطوة بخطوة. راقب قيم المتغيرات، Call Stack، وScope. هذه الطريقة مفيدة عندما يكون الخطأ نتيجة تغير قيمة عبر عدة دوال.
يمكن أيضًا استخدام conditional breakpoint عندما تريد التوقف فقط إذا تحققت حالة معينة، مثل وصول items.length إلى صفر.
راقب Network عند مشاكل API
إذا كان الكود يعتمد على fetch، افتح تبويب Network وتحقق من الطلب نفسه: URL، method، status code، headers، وResponse. افصل بين فشل الطلب وفشل معالجة البيانات بعد وصولها.
const response = await fetch("/api/articles");
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();ميّز بين أنواع الأخطاء الشائعة
- SyntaxError: مشكلة في صياغة الكود تمنع تفسيره كما هو مكتوب.
- ReferenceError: استخدام اسم غير معروف في النطاق الحالي.
- TypeError: تنفيذ عملية غير مناسبة للقيمة الحالية، مثل الوصول إلى خاصية على
null. - أخطاء منطقية: الكود يعمل دون استثناء واضح لكنه يعطي نتيجة غير صحيحة.
تتبع القيمة قبل تتبع السطر
ظهور نتيجة غير صحيحة لا يعني بالضرورة أن السطر الذي يعرضها هو موضع الخطأ. ارجع بمسار القيمة خطوة خطوة: مصدرها، وأي تحويلات طرأت عليها، ومحتوى استجابة API، وما إذا كان التنفيذ قد سبق اكتمال تحميل البيانات.
استخدم خطوات صغيرة قابلة للاختبار
اختبر تفسيرًا واحدًا للمشكلة في كل محاولة، ثم أعد تنفيذ الخطوات نفسها. إجراء عدة تغييرات معًا يجعل من الصعب تحديد الإصلاح الحقيقي، وقد يخفي مشكلة أخرى. بعد التعديل، أعد اختبار الحالة التي كشفت الخطأ وحالة مشابهة لها.
Debugging في DOM والأحداث
عند عدم استجابة زر أو نموذج، تحقق أولًا من وجود العنصر ومن تسجيل المستمع على العنصر الصحيح. افحص أيضًا ما إذا كان السكربت يعمل قبل اكتمال DOM أو إذا كان هناك عنصر متغير بعد تحميل الصفحة.
const button = document.querySelector("#save");
if (!button) {
throw new Error("زر الحفظ غير موجود");
}
button.addEventListener("click", saveData);لا تخلط Debugging مع Clean Code
تحسين بنية الكود يساعد على منع بعض الأخطاء، لكنه ليس بديلًا عن تشخيص مشكلة محددة. إذا كانت مهمتك فهم تنظيم الملفات والمسؤوليات وقابلية الصيانة، راجع دليل JavaScript للكود القابل للصيانة. أما هذا المقال فيركز على اكتشاف الخطأ وتتبع سببه.
سير عمل عملي لإصلاح الخطأ
- أعد إنتاج المشكلة وسجل النتيجة المتوقعة والفعلية.
- اقرأ رسالة الخطأ وStack Trace.
- ضع Breakpoint أو أضف تسجيلًا مؤقتًا في نقطة مناسبة.
- افحص قيم المدخلات والبيانات والـDOM أو Network حسب نوع المشكلة.
- كوّن فرضية عن السبب الجذري واختبرها بتغيير صغير.
- أعد الاختبار للحالة الأصلية وحالات قريبة.
- نظف أدوات التشخيص المؤقتة ووثق السبب إذا كان الخطأ قابلًا للتكرار.
الخلاصة
تبدأ عملية التشخيص الجيدة بمشكلة يمكن تكرارها، ثم بجمع الأدلة قبل تغيير الكود. يساعدك Console وStack Trace وBreakpoints وNetwork على فحص ما يحدث فعليًا، وبناء تفسير للمشكلة، ثم التأكد من صحة الإصلاح.
مصادر وروابط موثوقة
من إعادة الإنتاج إلى الإصلاح
اكتب خطوات قصيرة تعيد إنتاج المشكلة، وحدد السلوك المتوقع والفعلي، ثم قلّص الحالة إلى أصغر مثال ممكن. استخدم نقاط التوقف لمراقبة القيم عند لحظة حدوث الخطأ بدل إضافة رسائل تسجيل كثيرة ثم نسيان حذفها.
- افتح Console واقرأ نوع الخطأ والملف والسطر.
- استخدم Sources ونقاط التوقف لفحص القيم ومسار التنفيذ.
- اختبر فرضية واحدة في كل مرة.
- أضف اختبارًا يمنع عودة الخطأ إن كان ذلك ممكنًا.
لا تخفِ الخطأ بمجرد التقاطه في try/catch؛ سجّل السياق المفيد أو اعرض حالة مفهومة للمستخدم، مع تجنب كشف أسرار أو بيانات شخصية في السجلات.
من العرض إلى السبب الجذري
عند ظهور خطأ، سجّل خطوات إعادة إنتاجه أولًا ثم افحص أول رسالة خطأ في Console. استخدم breakpoint قرب موضع تغير الحالة، وراقب قيم المتغيرات قبل السطر وبعده. لا تعالج العرض بإضافة شرط عشوائي قبل أن تفهم لماذا وصلت البيانات إلى الحالة غير المتوقعة.
- أعد إنتاج المشكلة بأقل عدد من الخطوات.
- افحص نوع القيمة وشكل البيانات القادمة من API.
- استخدم Network لمعرفة حالة الطلب والاستجابة.
- أضف اختبارًا يمنع عودة الخطأ بعد الإصلاح.
احذف سجلات التشخيص المؤقتة قبل النشر، ولا تطبع رموز الدخول أو بيانات المستخدم في Console.
منهجية Debugging لتحديد السبب الجذري
التصحيح الفعال لا يبدأ بتغيير الكود فور ظهور خطأ، بل بإعادة إنتاج المشكلة وجمع أدلة تساعد على عزل السبب. قد يكون الخطأ في البيانات أو توقيت التنفيذ أو الشبكة أو افتراض غير صحيح عن DOM. إذا غيّرت عدة أشياء في وقت واحد، يصبح من الصعب معرفة أي تعديل أصلح المشكلة أو تسبب في أثر جانبي.
| الأداة | متى تستخدمها؟ | ما الذي تبحث عنه؟ |
|---|---|---|
| Console | خطأ أو قيمة غير متوقعة | رسالة الخطأ والتتبع ومكان حدوثه |
| Breakpoints | تحديد أين يتغير السلوك | قيم المتغيرات وترتيب استدعاء الدوال |
| Network | طلب API أو ملف يفشل | status والرؤوس والوقت وجسم الاستجابة |
| Elements | عنصر يظهر أو يختفي بشكل خاطئ | DOM الفعلي والأنماط المطبقة |
| Performance | واجهة بطيئة أو متقطعة | مهام طويلة وعمليات مكلفة |
خطوات التحقيق
- اكتب خطوات قصيرة تكرر المشكلة، وحدد هل تحدث في كل مرة أم في ظروف معينة.
- افحص أول خطأ يظهر في Console، لأن الأخطاء اللاحقة قد تكون نتيجة له.
- ضع breakpoint قبل السطر المشكوك فيه وتابع القيم خطوة بخطوة.
- قارن المدخلات الفعلية بالافتراضات التي يعتمد عليها الكود.
- أصلح السبب، ثم أعد الاختبار على حالة النجاح والحالات الطرفية.
تعامل مع الأخطاء غير المتزامنة
قد تصل استجابات الشبكة بترتيب مختلف عن ترتيب إرسالها، خصوصًا في البحث أثناء الكتابة. استخدم آلية تمنع نتيجة قديمة من استبدال نتيجة أحدث، وألغِ الطلبات التي لم تعد مطلوبة عند ملاءمة ذلك. تأكد من التعامل مع حالات الرفض وأخطاء HTTP بشكل منفصل، ولا تترك Promise مرفوضة بلا معالجة.
حوّل الإصلاح إلى حماية مستقبلية
بعد حل المشكلة، أضف اختبارًا أو خطوة تحقق تمنع تكرارها. إذا كانت المشكلة في مدخل غير متوقع، أضف حالة اختبار لذلك المدخل. وإذا كانت في واجهة API، وثّق شكل الاستجابة وسلوك الخطأ. تجنب إزالة رسالة الخطأ لمجرد إخفائها؛ يجب أن يكون الإصلاح في السبب لا في الأثر المرئي فقط.
راجع دليل تصحيح JavaScript في Chrome DevTools ودليل Network panel لتطبيق خطوات تشخيص قابلة للتكرار.
من رسالة الخطأ إلى السبب الجذري: خطوات تصحيح عملية
ابدأ بتحديد ما إذا كان الخلل يحدث دائمًا أم في ظروف محددة. سجّل خطوات إعادة الإنتاج، والقيم ذات الصلة، والمتصفح أو البيئة، ثم افتح أول رسالة خطأ في وحدة التحكم. غالبًا ما تكون الرسائل اللاحقة نتيجة لخطأ سابق، لذلك افحص أول موضع فشل بدل تعديل كل سطر يظهر في السجل.
استخدم نقاط التوقف بدل الإكثار من console.log
ضع breakpoint قبل السطر الذي يفشل، وراقب القيم وأنواعها وترتيب استدعاء الدوال. افحص Call Stack لمعرفة المسار الذي أدى إلى المشكلة، واستخدم Network عندما يرتبط الخلل بطلب HTTP. يمكن للسجلات المؤقتة أن تساعد، لكن احذف ما لم يعد ضروريًا وتجنب تسجيل بيانات شخصية أو رموز سرية في الإنتاج.
اختبر الفرضية بأصغر تغيير
بعد تحديد سبب محتمل، غيّر شيئًا واحدًا ثم أعد الخطوات نفسها. إذا أصلحت المشكلة، تحقق من أن الحل لا يكسر حالة أخرى؛ فقد يخفي التحقق الإضافي خللًا في مصدر البيانات بدل إصلاحه. عند التعامل مع القيم، اختبر null وundefined والنص الفارغ والقيم العددية وحدود المصفوفة. وعند التعامل مع الشبكة، اختبر النجاح والفشل والمهلة والاستجابة غير المتوقعة.
حوّل المشكلة إلى اختبار
بعد إصلاح الخطأ، أضف اختبارًا أو حالة تحقق تمنع عودته. اكتب ملخصًا للسبب الجذري، لا للأعراض فقط، خاصة إذا كان الخلل في حالة مشتركة أو في ترتيب تنفيذ غير متزامن. إذا كان من الصعب إعادة إنتاجه، أضف تسجيلًا تشخيصيًا محدودًا يحترم الخصوصية ويجمع الحد الأدنى من المعلومات اللازمة.
راجع دليل Chrome DevTools لتصحيح JavaScript ومرجع MDN لأخطاء JavaScript. التوثيق الجيد للخطأ يجعل الإصلاح أسرع في المرة التالية.
حوّل إصلاح الخطأ إلى حماية دائمة
بعد حل المشكلة، أعد تشغيل خطوات إعادة الإنتاج نفسها ثم جرّب الحالات القريبة منها. إذا كان الخلل بسبب قيمة غير معرفة، اختبر القيم الفارغة والأنواع غير المتوقعة. وإذا كان السبب توقيت طلبات غير متزامنة، جرّب وصول الاستجابات بترتيب مختلف. هذه الاختبارات تمنع إصلاحًا يعمل في المثال الوحيد لكنه يفشل عند تغير الظروف.
اجعل السجل مفيدًا وآمنًا
في الأخطاء التي يصعب تكرارها، أضف تسجيلًا محدودًا يوضح نوع العملية ورمز الحالة والوقت، مع تجنب تسجيل بيانات شخصية أو رموز جلسات. استخدم مستويات logging مناسبة، وأزل السجلات المؤقتة التي تكشف تفاصيل غير ضرورية. لا تجعل التطبيق يتجاهل الاستثناء بصمت؛ اعرض للمستخدم رسالة مناسبة وسجل التفاصيل اللازمة للمطور في المكان الآمن.
شارك السبب الجذري والإصلاح مع الفريق إذا كان من المحتمل أن يظهر الخلل في وحدات أخرى. هذا يقلل تكرار الأخطاء ويحوّل تجربة التصحيح إلى تحسين في جودة المشروع.
أنشئ مصفوفة حالات قبل إغلاق البلاغ
لا تختبر الحالة التي فشلت فقط؛ اختبر أيضًا حالة النجاح المعتادة وحالة الحد وقيمة غائبة وحالة فشل خارجي إن كانت ذات صلة. عند إصلاح مشكلة في واجهة، تحقق من عرض الهاتف ولوحة المفاتيح، وعند إصلاح طلب شبكة اختبر الاستجابة غير الناجحة وانقطاع الاتصال. سجّل النتيجة المتوقعة والفعلية، ثم أضف اختبارًا دائمًا إذا كان المشروع يوفر إطار اختبارات. هذه الخطوة تقلل احتمال عودة الخطأ بعد إعادة تنظيم الكود.
الخلاصة
الخلاصة: أعد إنتاج الخطأ، واجمع الأدلة من Console وNetwork وbreakpoints، ثم أصلح السبب واختبر الانحدار. تقرير واضح واختبار مكرر يمنعان عودة المشكلة.