لربما صادفتك بعض المشاكل عندما بنيت في مقال سابق لعبة تخمين الرقم الصحيح، أو وجدت أن هذه اللعبة لا تعمل بالشكل المطلوب! لهذا سنفرد هذا المقال لمساعدتك في البحث عن المشكلات البرمجية التي قد تصادفك وإيجاد حل لها، من خلال تزويدك ببعض التلميحات والنصائح عن كيفية إيجاد وإصلاح اﻷخطاء.
ننصحك قبل المتابعة في قراءة هذا المقال بالاطلاع على بعض المقالات السابقة مثل:
- أساسيات علوم الحاسوب
- أساسيات HTML
- أساسيات عمل CSS
- أساسيات جافا سكريبت
أنواع اﻷخطاء البرمجية
عندما ترتكب خطأ عند كتابة الشيفرة ستواجه عمومًا نوعين من اﻷخطاء:
- أخطاء في الصياغة (قواعدية) syntax error: وتنتج عن الخطأ في كتابة التعليمات مما يسبب توقف عمل البرنامج تمامًا أو توقف جزء منه، وستظهر عادة بعض رسائل الخطا أيضًا. وهي عادة قابلة للإصلاح إن كنت تألف العمل على اﻷدوات المناسبة للتنقيح وتعرف ما تعنيه رسالة الخطأ.
- أخطاء منطقية logic errors: في هذه الحالة تكون الصياغة صحيحة، لكن الشيفرة لا تعمل كما ينبغي، أي أن البرنامج يُنفّذ بنجاح لكنه يعطي نتائج غير صحيحة. وهي أخطاء أصعب إصلاحًا لعدم توفر رسائل أخطاء توجهك إلى مصدر الخطأ.
فالعملية إذًا ليست بهذه السهولة. وما ستراه عندما تتعمق في البرمجة وجود تفاصيل أخرى لأنواع اﻷخطاء، لكن التصنيف السابق هو كل ما تحتاجه في هذا المستوى المبكر من مسيرتك، وسنعمل على هذين النوعين في هذا المقال.
مثال عن خطأ
حتى نبدأ العمل، عليك أن تعود إلى لعبة خمّن الرقم الصحيح، لكن ما سنفعله أننا سنعود إلى نسخة أخرى من اللعبة ارتُكبت فيها أخطاء عمدًا. لهذا حمّل نسخة منها.
- افتح الملف ضمن المحرر النصي وضمن المتصفح.
- جرّب أن تلعب، وسترى أن اللعبة لا تعمل عند النقر على زر "Submit guess".
ملاحظة: ربما قد حصلت بالفعل على نسخة لا تعمل عند تجريب كتابة الشيفرة بنفسك، لكننا نريد منك أن تعمل على نسختنا في هذا المقال كي تتعلم التقنيات التي نقدمها لحل المشاكل ثم يمكنك العودة بعدها لتصحيح أخطاء نسختك.
ما سنفعله حاليًا هو الانتقال إلى "طرفية المطوّر" لنرى إن كانت تعرض أية أخطاء صياغة كي نحاول إصلاحها، وهذا ما ستتعلمه تاليًا.
إصلاح أخطاء الصياغة
قد تكون كتبت بعض أوامر جافا سكريبت في طرفية جافا سكريبت ضمن أدوات مطوري ويب DevTools، لكن الفائدة الأكبر من كتابة الشيفرة في أدوات المطور هي رسائل أخطاء الصياغة التي تعرضها لك الطرفية عند وقوع الخطأ. لنبدأ باصطياد اﻷخطاء إذًا!
1.انتقل إلى النافذة التي ظهرت number-game-errors.html
ثم افتح طرفية جافا سكريبت وسترى رسالة خطأ التالية:
2. السطر اﻷول من الرسالة هو التالي
Uncaught TypeError: guessSubmit.addeventListener is not a function number-game-errors.html:86:15`
يخبرنا الجزء الأول عن سبب الخطأ ويخبرنا الجزء الثاني عن موقع الخطأ في الشيفرة وهو السطر 86، المحرف 15 في الملف "number-game-errors.html".
3. إن ألقينا نظرة على موقع الخطأ في السطر 86 سنجد السطر التالي:
guessSubmit.addeventListener("click", checkGuess);
تحذير: قد لا يكون الخطأ موجودًا لديك في السطر 86 في حال كنت تستخدم موسّعًا يُشغّل خادمًا على جهازك، فقد يسبب ذلك إقحام شيفرة إضافية إلى شيفرتك، لهذا قد تشير أدوات مطوري ويب إلى مكان للخطأ مختلف عن السطر 86.
4. ينص الخطأ على أن الشيفرة لا تمثل دالة، وهذا يعني أن الدالة التي نستدعيها لم يميزها مفسّر جافا سكريبت. وغالبًا ما يكون السبب خطأ في كتابة شيء ما. فإن لم تكن متأكدًا من الكتابة الصحيحة لتعليمة أو صياغة معينة فمن الأفضل مراجعة توثيق جافا سكريبت على موسوعة حسوب والبحث عن "addeventListener".
5. بالعودة إلى التوثيق السابق نرى أن الخطأ كان في تهجئة اسم الدالة التي تُكتب بالشكل addEventListener
وليس بالشكل addeventListener
ومن مميزات لغة جافا سكريبت أنها حساسة لحالة الأحرف، وبالتالي تصحيح كتابة اسم الدالة سيحل المشكلة. نفّذ ذلك وتأكد من حل المشكلة.
جولة ثانية على أخطاء الصياغة
1. احفظ التغييرات على صفحتك وحدّث المتصفح وسترى أن الخطأ قد زال.
2. لو حاولت مجددًا تخمين رقم ثم النقر على زر اﻹرسال سترى خطأً آخر:
3. الخطأ هذه المرة هو التالي:
Uncaught TypeError: can't access property "textContent", lowOrHi is null
قد تجد رسالة مختلفة هنا وفقًا للمتصفح الذي تستخدمه. فالرسالة السابقة هي ما يعرضه متصفح فايرفوكس، أما رسالة خطأ متصفح كروم مثلًا هي:
Uncaught TypeError: Cannot set properties of null (setting 'textContent')
الخطأ ذاته لكن كل متصفح يصفه بطريقة مختلفة عن اﻵخر.
ملاحظة: لا يُعرض هذا الخطأ فور تحميل الصفحة لأنه يحدث ضمن الدالة كتلة ()checkGuess
. وكما سنرى في مقالات قادمة أن الشيفرة داخل الدوال تنفذ في سياق منفصل عن بقية الشيفرة، وبهذه الحالة لن تعمل الشيفرة ضمن الدالة ولن يقع الخطأ حتى تّنفّذ الدالة ()checkGuess
في السطر 86.
4. ألق نظرة على الخطأ في السطر 80 وسترى الشيفرة التالية:
lowOrHi.textContent = "Last guess was too high!";
5. تحاول الشيفرة في هذا السطر إسناد سلسلة نصية إلى الخاصية textContent
للمتغير lowOrHi
لكن اﻷمر لا ينجح لأن المتغيّر لا يتضمن ما يُفترض أنه يتضمنه. لهذا حاول أن تبحث عن ورود آخر لهذا المتغّير وستجده في السطر 49:
const lowOrHi = document.querySelector("lowOrHi");
نحاول في هذا السطر أن نسند إلى lowOrHi
مرجعًا إلى عنصر من عناصر صفحة HTML، لنلقي إذًا نظرة على ما يحتويه هذا المتغير بعد هذه العملية من خلال كتابة الأمر التالي في السطر 50:
console.log(lowOrHi);
تطبع هذه التعليمة قيمة المتغير lowOrHi
على الطرفية.
6. احفظ التغييرات وحدّث المتصفح وسترى نتيجة التعليمة ()console.log
على النحو التالي:
إن قيمة هذا المتغير هي null
لهذا عرض متصفح رسالة الخطأ مشيرًا إلى أن lowOrHi is null
. لهذا هنالك خطأ بالتأكيد في السطر 49. إذ تعني القيمة null
لاشيء أو لا قيمة، وأخفقت في النهاية عملية إسناد مرجع إلى التغيّر.
7. لنفكر بسبب حدوث مشكلة السطر 49، إذ يحتوي هذا السطر التابع ()document.querySelector
للحصول على مرجع إلى عنصر من خلال البحث عن محدد CSS المطابق. ولو بحثا عن العنصر المطلوب لوجدنا أنه الفقرة النصية
<p class="lowOrHi"></p>
8. إذا ما نريده هو محدد صنف class selector يبدأ سمه بنقطة .
، لكن المحدد الذي مُرر إلى التابع ()querySelector
في السطر 49 كان بلا نقطة، وقد يكون ذلك سبب المشكلة لهذا جرّب إصلاح اﻷمر بتحويل lowOrHi
إلى lowOrHi.
9. جرّب حفظ التغييرات وتحديث الصفحة، ومن المفترض حينها أن تكون نتيجة التعليمة ()console.log
هي العنصر <p>
، وهكذا نكون قد أصلحنا خطأ آخر. يمكنك اﻵن حذف السطر ()console.log
أو إبقاءه كمرجع -اﻷمر يعود إليك-.
جولة ثالثة على أخطاء الصياغة
1. إن حاولت تجريب اللعبة مجددًا ستجد أن إصلاحاتك ناجحة وستعمل بشكل رائع حتى اللحظة التي تنهي فيها اللعبة بأن تخمّن الرقم الصحيح أوتنتهي محاولاتك.
2. ستخفق اللعبة مجددًا في هذه المرحلة وسيظهر من جديد الخطأ الأول "TypeError: resetButton.addeventListener is not a function"، لكن مصدره هذه المرة السطر 94.
3. بالعودة إلى هذا السطر والتدقيق فيه نجد أن الخطأ المرتكب هو نفسه الخطأ المرتكب في المرة اﻷولى، لهذا غيّر addeventListener
إلى addEventListener
.
الخطأ المنطقي
يجب أن تعمل اللعبة جيدًا بعد تطبيق اﻹصلاحات السابقة جميعها، لكن بعد أن تجرب اللعبة عدة مرات ستلاحظ بلا شك أن الرقم العشوائي الذي تختاره اللعبة وعليك تخمينه هو دائما (1). وبالتأكيد لا نريد أن تكون اللعبة بهذا الشكل! لا شك إذًا أن هناك خطأ ما في منطق اللعبة لأنها لا تعيد أية أخطاء لكنها في المقابل لا تعطي النتيجة المتوقعة منها.
1. ابحث عن المتغيّر randomNumber
والأسطر التي ضُبط فيها بداية وستجد في السطر 45 تقريبًا التصريح عن المتغّير وإسناد قيمة له:
let randomNumber = Math.floor(Math.random()) + 1;
2. ستجد في السطر 113 الأمر الذي يوّلد الرقم العشوائي قبل كل بداية لعبة
randomNumber = Math.floor(Math.random()) + 1;
3. للتحقق أن هذه اﻷسطر هي مصدر المشكلة، سنحاول استخدام التعليمة ()console.log
أسفل كل من السطرين السابقين كالتالي:
console.log(randomNumber);
4. احفظ التغييرات وحدّث المتصفح ثم جرّب أن تلعب بعض الجولات وسترى أن قيمة المتغيّر randomNumber
تُسجّل (1) دائمًا في الطرفية.
العمل على إصلاح اﻷخطاء المنطقية
لنتأمل كيفية عمل التابع ()Math.random
في محاولة فهم الخلل، إذ يوّلد هذا التابع رقمًا عشريًا عشوائيًا بين 0 و 1 مثل 0.5463723462
. ثم نمرر العدد العشوائي الناتج إلى تابع آخر هو ()Math.floor
والذي يقرّب قيمة العدد العشري إلى العدد الصحيح اﻷقل منه مباشرة، ثم نضيف إلى الناتج العدد 1.
Math.floor(Math.random()) + 1;
إن تقريب عدد عشري بين 0 و 1 إلى أقرب عدد صحيح أصغر منه مباشرة يعطي الرقم 0 دائمًا وعند إضافة الرقم 1 سيكون الناتج 1 دائمًا! لهذا من الواضح أن علينا ضرب العدد العشوائي بالعدد 100 للحصول على عدد عشري بين 0 و 100 ثم نقرّبه إلى أقرب عدد صحيح أصغر منه فستكون النتيجة عدد عشوائي صحيح بين 0 و 99:
Math.floor(Math.random() * 100);
نضيف بعد ذلك الرقم 1 لنحصل على عدد بين 1 و100
Math.floor(Math.random() * 100) + 1;
جرّب أن تعدّل السطرين اللذان يضمان المنطق السابق ثم احفظ التغييرات وحدّث المتصفح وسترى أن اللعبة تعمل اﻵن كما نريد.
أخطاء شائعة أخرى
ستقع في العديد من الأخطاء الشائعة عند كتابة شيفرتك، لهذا سنلقي نظرة على أهمها في الأقسام التالية.
الخطأ SyntaxError: missing ; before statement
يشير هذا الخطأ عمومًا إلى أنك أغفلت الفاصلة المنقوطة ;
في نهاية أحد أسطر الشيفرة، وقد يكون الخطأ أحيانًا أكثر غموضًا. فلو بدلنا السطر التالي ضمن الدالة ()checkGuess
const userGuess = Number(guessField.value);
إلى السطر التالي
const userGuess === Number(guessField.value);
سيعرض المتصفح الخطأ السابق لأنه يعتقد بأن تحاول عمل شيء مختلف. لهذا عليك الانتباه إلى عدم الخلط بين عامل الإسناد =
الذي يُسند قيمة إلى متغير ما، وبين عامل المساواة المنطقي ===
الذي يختبر تساوي قيمة مع قيمة أخرى ثم يعيد قيمة منطقية قيمتها صحيح true
أو خطأ false
.
تخبرك اللعبة أنك فزت دائمًا سواء كان تخمينك صحيحًا أو خاطئًا
قد يكون هذا الخطأ نتيجة للخلط بين عاملي اﻹسناد والمساواة. فلو غيرنا السطر التالي ضمن الدالة ()chsckGuess
if (userGuess === randomNumber) {
إلى السطر التالي:
if (userGuess = randomNumber) {
سيعيد الاختبار في هذه الحالة القيمة true
دائمًا وستخبرك اللعبة عندها أنك فائز دائمًا. انتبه لذلك!
الخطأ SyntaxError: missing ) after argument list
هذا الخطأ بسيط نوعًا ما، ويعني عمومًا أنك أغفلت كتابة قوس الإغلاق في نهاية دالة أو تابع عند استدعائه.
الخطأ: SyntaxError: missing : after property id
يرتبط هذا الخطأ عادة بإعداد كائن جافا سكريبت بطريقة غير صحيحة، لكننا حاولنا إظهاره هنا بتبديل هذا السطر:
function checkGuess() {
بالسطر التالي
function checkGuess( {
إذ سيعتقد المتصفح انك تحاول تمرير محتوى دالة كوسيط إلى دالة أخرى. عليك الانتباه إلى اﻷقواس جيدًا!
الخطأ: SyntaxError: missing } after function body
وهو خطأ بسيط يشير إلى إغفال أحد القوسين المعقوصين في بنية دالة أو كتلة شرطية، يمكنك توليد هذا الخطأ بحذف القوس المعقوص من نهاية
الدالة ()checkGuess
الخطأ: '*SyntaxError: expected expression, got '*string
أو SyntaxError: unterminated string literal
تعني هذه اﻷخطاء عمومًا أنك أغفلت أحد علامات التنصيص التي تغلق سلسلة نصية أو تبدؤها. ففي الخطأ اﻷول يستبدل المتصفح القيمة string بالقيمة غير المتوقعة التي وجدها بدلًا من إشارة التنصيص في بداية السلسلة النصية. أما الخطأ الثاني فيعني أنك لم تنهي السلسلة النصية بإشارة تنصيص.
وفي جميع اﻷخطاء التي قد تواجهك، فكّر بالطريقة التي اتبعناها في أمثلتنا لحل اﻷخطاء. فعندما يقع الخطأ، انتقل إلى السطر الذي وقع فيه والذي تشير إليه طرفية أدوات مطوري ويب وحاول أن تتفحص ما يمكن أن يكون خاطئًا. وتذكر دائمًا أنه ليس من الضرورة أن تكون اﻷخطاء في نفس السطر، وأن الخطأ قد لا ينتج بالضرورة عن نفس اﻷسباب التي تحدثنا عنها في مقالنا.
الخلاصة
هكذا نكون قد تعرفنا على أساسيات تتبع اﻷخطاء البسيطة في جافا سكريبت. وتذكر أنه ليس من السهل دائمًا اكتشاف الخطأ في الشيفرة البرمجية، لكن ما قدمناه في هذه المقالة قد يوفر عليك ساعات من العناء ويسرّع وتيرة اكتشافك اﻷسباب المحتملة للأخطاء وخاصة في بداية رحلتك في تعلم البرمجة.
ترجمة -وبتصرف لمقال What went wrong troubleshooting JavaScript
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.