الـ Console ليست مجرد log
console.log جافا سكريبت هو أول أداة تصحيح أخطاء يتعلمها أغلب المطوّرين، وآخر أداة يتخلّى عنها الكثيرون. صحيح أنها تؤدي الغرض، لكن كائن console يحتوي على عشرات الدوال الأخرى التي تجعل تصحيح أخطاء جافا سكريبت أسرع وأوضح. وبمجرّد أن تتقن أدوات Chrome DevTools الحقيقية — من نقاط التوقف، ومكدّس الاستدعاءات، وتعابير المراقبة — ستجد نفسك تلجأ إلى log أقل بكثير مما اعتدت عليه.
لنبدأ بجولة سريعة:
الأربعة كلها تكتب في نفس الـ console، لكن المتصفحات تميّزها بأشكال مختلفة: التحذيرات تظهر بخلفية صفراء، والأخطاء بلون أحمر مع أيقونة، وكلاهما يُرفق مع تتبّع كامل لسلسلة الاستدعاءات (stack trace). كما توفّر فلاتر في DevTools لإخفاء أو عزل كل مستوى على حدة، وهذا أمر ضروري حين يبدأ تطبيقك في إنتاج مئات الرسائل.
أسهل طريقة لطباعة عدّة قيم دفعة واحدة
حين تريد معاينة عدة متغيّرات في نفس الوقت، تجنّب دمجها داخل سلسلة نصية واحدة. مرّرها كوسائط منفصلة، أو الأفضل من ذلك، ضعها داخل كائن واحد حتى يحمل كل متغيّر اسمه بوضوح:
الحيلة { user, count } تستفيد من الاختصار الخاص بالكائنات في جافا سكريبت: اسم المتغير يصبح هو المفتاح تلقائياً. في وحدة التحكم سترى { user: {...}, count: 3 } ويمكنك توسيع user لفحص محتواه بالتفصيل، دون أن تفقد بنية الكائن نتيجة تحويله إلى نص.
استخدام console.table مع مصفوفات الكائنات
حين تحاول قراءة مصفوفة من الكائنات، فإن console.log يعرضها بشكل مكدّس ومربك. أما console.table فيعرضها في صورة جدول حقيقي ومنظم:
في طرفية المتصفح، يعرض الاستدعاء الأول كل الصفوف الثلاثة في جدول تفاعلي يمكن فرز أعمدته، بينما يقتصر الاستدعاء الثاني على عمودَي name و role فقط. هذه قفزة نوعية في الراحة عند التعامل مع أي بيانات ذات شكل جدولي، سواء كانت استجابات من API أو نتائج استعلامات أو ملفات CSV بعد تحليلها.
الفرق بين console.dir و console.log في جافا سكريبت
يبدو الاثنان متشابهين للوهلة الأولى، لكنهما يتصرفان بشكل مختلف تمامًا مع عناصر DOM:
const el = document.querySelector("button");
console.log(el); // يطبع HTML: <button>Click me</button>
console.dir(el); // يطبع عرض كائن JS مع جميع الخصائص
log يعرض العناصر على شكل HTML، بينما console.dir يكشف لك الكائن كما هو فعلياً — كل خاصية، وكل معالج أحداث، وكل مرجع للأنماط المحسوبة. لمّا تحتاج تعرف الدوال والخصائص المتاحة على عنصر معيّن، استخدم dir دون تردد.
تجميع الرسائل المترابطة في الكونسول
جلسات تصحيح الأخطاء الطويلة تُغرق الكونسول بكمّ هائل من الرسائل المتناثرة. هنا يأتي دور console.group و console.groupEnd اللذين يلفّان الرسائل المترابطة داخل كتلة واحدة قابلة للطيّ:
كل استدعاء ينشئ مجموعة مُسمّاة يمكنك طيّها. استخدم console.groupCollapsed إذا أردت أن تبدأ المجموعات مطويّة افتراضيًا، وهذا مفيد عندما ترغب بفتح المجموعات المشبوهة فقط.
قياس زمن تنفيذ الكود باستخدام console.time
لو أردت فحصًا سريعًا للأداء، فمن الصعب أن تجد بديلًا أفضل من console.time و console.timeEnd:
يجب أن تتطابق التسمية (label) في time و timeEnd. ويمكنك تشغيل عدة مؤقتات في الوقت نفسه ما دامت تسمياتها مختلفة. لكن إذا كان الأمر يتعدى سؤال "هل هذه الحلقة بطيئة؟"، فالأفضل أن تنتقل إلى تبويب Performance في DevTools، لأنه يسجّل خطًا زمنيًا كاملًا مع مخطط الشعلة (flame chart).
التحققات (Assertions): اطبع فقط عند وجود مشكلة
الدالة console.assert لا تطبع شيئًا إلا إذا كان الشرط خاطئًا (falsy). إنها طريقة هادئة تتيح لك ترك فحوصات منطقية داخل الكود دون إغراق الـ console بالرسائل حين يسير كل شيء على ما يرام:
مفيدة للثوابت التي يُفترض أن تتحقق دائمًا. لكنها غير مناسبة للحالات التي قد تفشل فعلًا لسبب مشروع — في هذه الحالات، ارمِ خطأً بدلاً من ذلك.
طباعة مسار الاستدعاءات عند الحاجة
تطبع console.trace مسار الاستدعاءات الحالي (call stack) دون أن ترمي أي خطأ. وهي مفيدة جدًا حين تحاول معرفة مَن الذي استدعى دالة معينة:
المُخرجات تظهر inner → outer → (top level). في تطبيق حقيقي، هذي الطريقة اللي تكتشف فيها إن مُعالِج النقر اللي تحاول تصحّحه يتم استدعاؤه من ثلاثة أماكن مختلفة.
جملة debugger لإيقاف التنفيذ
أسرع طريقة لإيقاف تنفيذ كود جافا سكريبت مؤقتًا هي كلمة واحدة:
function computeTotal(items) {
const subtotal = items.reduce((s, i) => s + i.price, 0);
debugger;
return subtotal * 1.08;
}
عندما تكون أدوات المطور مفتوحة، فإن debugger; تتصرّف تماماً مثل نقطة توقف — يتوقّف التنفيذ عند هذا السطر ويظهر لك المصحّح بكامل إمكانياته: المتغيّرات المتاحة في النطاق الحالي، وسجلّ الاستدعاءات (call stack)، وأزرار التنقّل بين الأسطر (step over وstep into)، وإمكانية تقييم أي تعبير مقابل الحالة الراهنة للبرنامج. أمّا إذا كانت أدوات المطور مغلقة، فإن debugger; لا تفعل أي شيء.
أوّل مرة تستخدم فيها مصحّحاً حقيقياً بدلاً من console.log، ستشعر وكأنك اكتسبت قوّة خارقة. تستطيع رؤية كل متغيّر في النطاق دون أن تقرّر مسبقاً ماذا تطبع. تستطيع التنقّل بين الشروط ومراقبة أي فرع يتم تنفيذه فعلاً. إصلاح خطأ متعِب سيتحوّل من دقائق إلى ثوانٍ.
لا تنسَ حذف سطر debugger قبل عمل commit — أو الأفضل من ذلك، ضع نقاط التوقف مباشرة داخل أدوات المطور بالنقر على رقم السطر في لوحة Sources.
حيل في Chrome DevTools تستحقّ أن تعرفها
هناك ميزات يتجاهلها الكثيرون لسنوات:
- نقاط التوقف الشرطية (Conditional breakpoints): اضغط بزر الفأرة الثانوي على رقم السطر في Sources وضع شرطاً مثل
user.id === 42. لن تعمل نقطة التوقف إلا عندما يتحقّق الشرط. - Logpoints: من القائمة نفسها، اختر "Add logpoint". تطبع رسالة دون إيقاف التنفيذ ودون تعديل شيفرتك.
$_داخل الـ console: آخر تعبير تم تقييمه. نفّذ أي شيء، ثم استخدم$_للحصول على نتيجته.$0: العنصر المحدَّد حالياً في لوحة Elements. مثلاً$0.textContentيفحص محتوى العنصر الذي نقرت عليه.- Copy as object: اضغط بزر الفأرة الثانوي على أي قيمة في الـ console واختر "Store as global variable". ستحصل على
temp1وtemp2وهكذا لتتعامل معها. - Network panel ← Copy as fetch: يحوّل أي طلب إلى استدعاء
fetch()جاهز للصق داخل الـ console وتعديله.
لا شيء من هذه الحيل ضروري، لكن جميعها توفّر عليك الكثير من الوقت بمجرد أن تصبح جزءاً من ذاكرتك العضليّة.
تنظيف الشيفرة قبل النشر
استدعاءات console.log المتروكة لا تضرّ أثناء التطوير، لكنها تصبح ضوضاء مزعجة في الإنتاج. بعض العادات التي تفيد:
- فعّل قاعدة lint مثل (
no-consoleفي ESLint) لرصد الطباعات المنسيّة، مع استثناءwarnوerror. - غلّف الطباعات المطوّلة بشرط مثل:
if (process.env.NODE_ENV !== "production") console.log(...). - استخدم
console.debugللمخرجات التفصيلية — معظم أدوات التجميع (bundlers) وأنظمة تجميع السجلّات قادرة على فلترتها. - والأفضل من كل ذلك: استخدم وحدة تسجيل صغيرة خاصة بك (أو مكتبة مثل
debug) حتى تتمكّن من تشغيل وإطفاء فئات السجلات دون تعديل الشيفرة.
التسجيل ليس مجّانياً. كل استدعاء يقوم بتسلسل (serialization) وسائطه وكتابتها في المخزن المؤقت. وداخل حلقة ساخنة، قد يُبطِئ console.log منسيٌّ أداء برنامجك بشكل ملحوظ.
ما التالي؟ التعابير النمطية
ستقضي وقتاً طويلاً في تصحيح أخطاء شيفرات معالجة النصوص، وكثير من هذه الشيفرات يعتمد على التعابير النمطية (regex) — وهي أكثر خاصية مضغوطة وأكثرها غموضاً في اللغة. الفصل التالي يبدأ بجولة هادئة داخل التعابير النمطية في جافا سكريبت والدوال التي تستخدمها.
الأسئلة الشائعة
ما الفرق بين console.log و console.dir؟
console.log يطبع القيم بالتنسيق الافتراضي للمتصفح، وهذا يعني أن عناصر DOM تظهر على هيئة HTML. أما console.dir فيعرض دائمًا كائن JavaScript بكل خصائصه، وهو ما تحتاجه فعلًا عندما تريد فحص خصائص العنصر بدلًا من شكله في الـ markup.
كيف أصحّح أخطاء JavaScript في Chrome DevTools بدون console.log؟
افتح لوحة Sources، اختر ملفك، ثم اضغط على رقم السطر لوضع نقطة توقف (breakpoint). عند وصول التنفيذ إلى هذا السطر سيتوقف تلقائيًا، ويمكنك حينها فحص المتغيرات والتنقل سطرًا بسطر وتقييم التعابير مباشرة في الـ console. وكبديل سريع، يمكنك كتابة debugger; داخل الكود نفسه ليعمل كنقطة توقف من داخل المصدر.
كيف أقيس زمن تنفيذ كود JavaScript؟
ضع الكود بين console.time('label') و console.timeEnd('label') مع نفس الـ label في الطرفين، وسيطبع الـ console الزمن المستغرق بالميلي ثانية. وإذا أردت تحليلًا أعمق، استخدم لوحة Performance في DevTools لتسجيل flame chart يوضّح كل ما جرى تنفيذه.
ما فائدة console.table؟
console.table يعرض المصفوفات والكائنات على شكل جدول قابل للفرز داخل الـ console، وهو أسهل بكثير في القراءة من تنسيق الكائنات المتداخلة. وهو مثالي لمصفوفات الكائنات، حيث يصبح كل كائن صفًا ومفاتيحه أعمدة. ويمكنك تمرير وسيط ثانٍ لتحديد الأعمدة التي تريد إظهارها فقط.