מה השתבש? - פתרון שגיאות ב-JavaScript

כשבנינו את המשחק ״שחק את המספר״ במאמר הקודם, בטח שמתם לב שהוא לא עבד. מאמר זה לספק לכם הבנה כיצד למצוא שגיאות ב-JavaScript ולתקן אותן.

ידע מוקדם: הבנה בסיסית של מחשב, הבנה בסיסית של  HTML, CSS ו - JavaScript.
מטרה:

הכרת חלק מהאפשרויות ללתיקון שגיאות בקוד שלנו.

סוגי שגיאות

באופן כללי, כשאנחנו מבצעים משהו שגוי בקוד, יש שתי סוגי שגיאות עיקריות שאנחנו ניתקל בהן:

  • Syntax errors - שגיאות סינטקס: אלו בעיקרון שגיאות כתיב בקוד שכתבנו שגורמות לתוכנית לא לרוץ בכלל, או להפסיק לעבוד בשלב מסוים. בדרך כלל גם נקבל הודעת שגיאה מסויימת. שגיאות אלו בדר״כ קלות לתיקון, ככל ואנחנו מכירים את הכלים שעומדים לרשותנו ומה הודעת השגיאה אומרת.
  • Logic errors - טעויות לוגיות: אלו שגיאות שבהן הסינטקס היה נכון, כלומר כתבנו ללא שגיאות כתיב, אבל הקוד לא מבצע את מה שרצינו שיבצע. אלו שגיאות שקצת יותר מורכב לתקן אותן מאשר שגיאות סינטקס, שכן הן לרוב לא יקפיצו הודעת שגיאה.

אוקיי, זה לא כזה פשוט — יש חלוקות נוספות של סוגי השגיאות, וככל ונעמיק בחומר ננחקור אותן לעומק. לצרוך הלימוד כרגע, החלוקה הזו תספיק בהתחלה. 

דוגמא לשגיאה

לשם התחלה, חזרו למשחק ״נחש את המספר״ שלנו - רק שהפעם, השתמשו בקובץ שלנו שהוכנסו בו מכלתחילה מספר שגיאות. כנסו ל-Github והכינו לעצמכם עותק מקומי של number-game-errors.html (ראו זאת בדף אינטרנט כאן).

  1. לשם התחלה, פתחו את הקובץ המקומי בעורך הקוד ובדפדפן.
  2. נסו לשחק עם המשחק, אתם תשימו לב כשאתם לוחצים על כפתור - "Submit guess", הכפתור לא עושה כלום.

לתשומת לב: יכול להיות שהגרסה שאתם עבדתם עליה לא עובדת בעצמה, ואולי אותה תרצו לתקן. בכל מקרה, אנא השתמשו בגרסה שלנו לאורך המאמר הנוכחי, על מנת שתוכלו ללמוד את הטכניקות שאנחנו נלמד כאן. לאחר מכן, חזרו לדוגמא שלכם ונסו לתקן אותה אם היא אינה עובדת.

בשלב זה, ניעזר בקונסולה בדפדפן על מנת לראות את שגיאות הסינטקס, וננסה לתקן אותם. 

תיקון שגיאות סינטקס

בתחילת הקורס, הזנו מס׳ פקודות של JavaScript לתוך הקונסולה. יתרון נוסף של שימוש בקונסולה הוא העובדה שהקונסולה מחזירה לכם הודעות בדבר שגיאות כתיב כאשר אלו קיימות בקוד ה-JavaScript אשר נטען על ידי מנוע ה- JavaScript של הדפדפן. בוא נבדוק זאת:

  1. לך ללשונית שבה יש את הקובץ number-game-errors.html פתוח ופתחו את הקונסולה. אתם אמורים לראות שגיאה שמכילה את המלל הבאה:
  2. זו שגיאה דיי קלה לאיתור, והדפדפן מספק לם מס׳ נתונים על מנת לעזור לכם - צילום המסך לעיל לקוח מדפדפן Firefox אבל גם דפדפנים אחרים מספקים את אותו מידע. משמאל לימין, זו השגיאה:
    • ״x״ אדום אשר יעיד על כל שקיימת שגיאה.
    • הודעת שגיאה על מנת להראות שמשהו השתבש: "TypeError: guessSubmit.addeventListener is not a function".
    • טקסט וקישור "Learn More" אשר יוביל לדף MDN שמסביר מהי השגיאה - שימו לב שקישור זה קיים רק בפיירפוקס ולא בכרום.
    • השם של קובץ JavaScript אשר מקושר ללשונית ה-Debugger. אם נעקוב אחרי הקישור נראה את השורה המדויקת שבה התרחשה השגיאה.
    • מספר השורה שבה התרחשה השגיאה, ומספרו של התו באותה השורה שבו השגיאה נראתה לראשונה. במקרה שלנו, השגיאה התרחשה בשורה 86, ובתו מספר 3.
  3. אם נסתכל על שורה 86 בעורך הקוד שלנו, אנחנו נמצא את השורה הבאה:
    guessSubmit.addeventListener('click', checkGuess);
  4. הודעת שגיאה זו אומרת ש-"guessSubmit.addeventListener is not a function", אז אנחנו כנראה אייתנו משהו בצורה לא נכונה. אם אנחנו לא בטוחים לגבי האיות הנכון של כתיב/סינטקס מסויים, הפתרון הכי טוב זה להסתכל על המאפיינים של הסינטקס באתר של MDN. הדרך הכי טובה לעשות זאת היא לחפש "mdn name-of-feature" במנוע החיפוש האהוב עליכם. הנה קישור לדוגמא לחסוך לכם זמן ()addEventListener.
  5. אז, אם אנחנו מסתכלים על הדף, השגיאה הופיעה ככל הנראה מכיוון שאייתנו את שם המתודה לא נכון. זכרו ש- JavaScript היא case sensitive, וכל שינוי קטן באיות, או שימוש באותיות גדולות/קטנות לא בצורה נכונה, יגרום לשגיאה. שינוי של addeventListener לכתיב הנכון: addEventListener אמור לתקן את הבעיה.

לתשומת לבכם ראו גם: TypeError: "x" is not a function. זהו דף שלנו שמסביר בפרטים על השגיאה הזו.

שגיאות סינטקס - דוגמאות נוספות

  1. שמרו את הדף ורעננו אותו, אתם אמורים לראות שהשגיאה נעלמה.
  2. כעת, אם המשתמש ינסה להכניס ניחוש וללחוץ על הכפתור של Submit, תתקבל שגיאה נוספת:
  3. הפעם, השגיאה מדווחת עם ההודעה הבאה: "TypeError: lowOrHi is null", בשורה 78.
    לתשומת לבכם: Null הוא ערך מיוחד שמשמעותו הוא ״כלום״ או ״ללא ערך״. כך lowOrHi אמנם הוצהר ואותחל, אבל הוא ללא כל ערך, אין לו סוג ואין ערך שהוזן לתוכו.
    לתשומת לבכם: שגיאה זו לא הופיעה ברגע שהדף נטען שכן שגיאה זו נמצאת בתוך פונקציה - בתוך () { ... }checkGuess. אנו נלמד על פונקציות במאמרים הבאים, ואנו נלמד שקוד אשר נמצא בתוך פונקציה רץ בסקופ (מתחם) אחר ונפרד מהסקופ שרץ בו קוד מחוץ לפונקציה. במקרה הנוכחי - הקוד שבתוך הפונקציה אשר גרם לשגיאה לא רץ עד אשר הפונקציה ()checkGuess לא הופעלה בשורה 26.
  4. :הסתכלו על הקוד בשורה 78
    lowOrHi.textContent = 'Last guess was too high!';
  5. שורה זו מנסה לקבוע את ה-property - מאפיין:  textContent של המשתנה lowOrHi למחרוזת טקסט, אך זה לא יעבוד מכיוון ש-lowOrHi לא מכיל את הערך שהוא אמור להכיל. בו ננסה לחפש מופעים נוספים של lowOrHi בקוד שלנו. הפעם הראשונה שנמצא אותו תהיה בשורה 48:
    var lowOrHi = document.querySelector('lowOrHi');
  6. בשלב זה אנחנו מנסים להפוך את המשתנה שלנו להפנייה לאלמנט בדף ה-html, בוא ננסה לראות האם הוא מקבל את הערך null אחרי ההרצה של השורה הזו. הוסיפו את הקוד הבא לאחר שורה 49:
    console.log(lowOrHi);

    לתשומת לבכם: ()console.log מקבלת ארגומנט כלשהו ומדפיסה אותו לקונסולה. זוהי פונקציה מאוד שימושית לשם איתור שגיאות.

  7. שמור ורענן, ואתה אמור לראות ש-()console.log החזירו את התשובה הבאה בקונסולה: אנו רואים שהערך של lowOrHiהוא בעצם null בנקודה זו ולכן הבעיה היא עם שורה 48.
  8. ננסה לחשוב מהי הבעיה. שורה 28 משתמשת במתודה ()document.querySelector על מנת לקבל הפניה לאלמנט באמצעות בחירת סלקטור ה-CSS שלו. אם נסתכל בדף, אנחנו יכולים למצוא את האלמנט הרלוונטי:
    <p class="lowOrHi"></p>
  9. כאן אנחנו רואים שאנחנו צריכים סלקטור של קלאס, אשר מתחיל עם (.), אבל הסלקטור שמועבר במתודה ()querySelector בשורה 48 אינו מכיל נקודה. זו יכולה להיות הבעיה, נסו לשנות את lowOrHi ל- .lowOrHi וראו מה קורה.
  10. שמרו ורעננו את הדף ו-()console.log אמור להחזיר את האלמנט <p> שרצינו. 

לתשומת לב: ראו גם TypeError: "x" is (not) "y" -  דף ההסבר שלנו לפרטים נוספים על שגיאה זו.

שגיאות סינטקס נוספות

  1. אם תנסו לשחק עכשיו במשחק, אתם אמורים להצליח להתקדם יותר - אך עד שהמשחק יסתיים, בין אם ניחשתם את המספר האקראי הנכון ובין אם נגמרו לך הנסיונות.
  2. כשהמשחק נגמר, אנחנו מקבלים שגיאה מסוג - "TypeError: resetButton.addeventListener is not a function"! אשר מופיעה בשורה 94.
  3. אם נסתכל על שורה 94, נראה כי עשינו טעות באיות המתודה addEventListener ולכן נדרש לשנות את הקוד. להזכירכם, מתודות הן case senitive.

טעות לוגית

בשלב זה, המשחק אמור לעבוד טוב, אך עם זאת, אחרי כמה משחקים, אתם תשימו לב שהמספר האקראי שלנו הוא תמיד 0 או 1. זה לא ממש מה שהתכוונו.

יש בוודאות שגיאה לוגית במשחק איפשהו, אך אנחנו לא מקבלים הודעת שגיאה, המשחק עובד, אך לא כמו רצינו.

  1. נסו לחפש את המשתנה randomNumber ואת השורות בקוד שבהן אנחנו מגדירים אותו. הוא נמצא בשורה 44:
    var randomNumber = Math.floor(Math.random()) + 1;
    והשורה שבה אנחנו מייצרים מספר אקראי חדש לאחר כל משחק היא בשורה 113:
  2. randomNumber = Math.floor(Math.random()) + 1;
  3. על מנת לבדוק האם השורות האלו הן הבעיתיות, אנחנו ניעזר ב- ()console.log ונכניס את הקוד הבא מתחת לשתי שורות לעיל:
    console.log(randomNumber);
  4. שמרו ורעננו את הדף ותנסו לשחק מספר משחקים. אתם תראו ש- randomNumber שווה ל-1 בכל פעם שאנחנו מדפיסים אותו לקונסולה:

מעבר על הלוגיקה של המשחק

על מנת לתקן זאת, בואו נחשוב כיצד שורה זו עובדת. קודם כל, אנחנו קוראים ל-()Math.random, אשר מייצרת מספר עשרוני בין 0 ל-1, לדוגמא: 0.5676665434:

Math.random()

לאחר מכן אנחנו מעבירים את התוצאה של הפעלת פונקציית ()Math.random דרך הפונקציה ()Math.floor, אשר בתורה, מעגלת את המספר ולאחר מכן מוסיפים 1. אם לא נוסיף 1, פונקציה זו יכולה לעגל כלפי מטה ואז נקבל 0.

Math.floor(Math.random()) + 1

1.  אנחנו צריכים להכפיל את המספר האקראי שלנו ב-100 לפני שנעביר אותו הלאה. זה ייתן לנו מספר אקראי בין 0 ל-99:

Math.floor(Math.random()*100);

הוספת של 1, תאפשר לנו לקבל ערך בין 1 ל-100:

Math.floor(Math.random()*100) + 1;

נסו לשמור ולרענן את הדף ואתם תראו שהמשחק עושה מה שהוא צריך לבצע - מייצר מספר אקראי בין 0 ל-100.

שגיאות נפוצות נוספות

ישנן שגיאות נפוצות נוספות שאנחנו ניתקל בהן: 

SyntaxError: missing ; before statement

שגיאה זו לרוב אומרת ששכחנו לשים ; בסוף שורות הקוד, אבל היא יכולה להיות גם מסיבה אחרת. אם נשנה את השורה שנמצאת בתוך הפונקציה ()checkGuess :

var userGuess = Number(guessField.value);

לקוד הבא:

var userGuess === Number(guessField.value);

זה יקפיץ לנו הודעת שגיאה מכיוון ש- JavaScript חושבת שאתה מנסה לבצע משהו אחר. צריך תמיד לוודא שאנחנו לא מערבבים בין סימן השווה שנועד להשים ערך (=) לבין שלושת סימני השווה שנועדו לבדוק האם ערך אחד שווה לשני, ולהחזיר true/false:

לתשומת לב: ראה גם SyntaxError: missing ; before statement דף ההסבר שלנו לפרטים נוספים על שגיאה זו.

המשחק תמיד נותן למשתמש לנצח, לא משנה מה הניחוש שהמשתמש הכניס

זה יכול להיות עוד סימן לכך שעירבבנו בין סימן ההשמה לבין סימן הבדיקה. לדוגמא, אם אנחנו את השורה הבאה שנמצא בתוך()checkGuess:

if (userGuess === randomNumber) {

לקוד הבא:

if (userGuess = randomNumber) {

תוצאת המבחן תהיה תמיד true, מה שגורם לכך שתמיד הניחוש של המשתמש נכון.

SyntaxError: missing ) after argument list

זוהי שגיאה פשוטה יחסית - היא אומרת באופן כללי ששכחנו לשים סוגריים בסוף קריאה לפונקציה או מתודה:

לתשומת לבד: ראה גם SyntaxError: missing ) after argument list  - דף ההסבר שלנו לפרטים נוספים על שגיאה זו.

SyntaxError: missing : after property id

שגיאה זו בדרך כלל מתייחסת לכך שלא יצרנו אובייקט ל JavaScript בצורה נכונה. נסה לשנות את הקוד הבא:

function checkGuess() {

לקוד הבא:

function checkGuess( {

זה גרם לדפדפן לחשוב שאנחנו מנסים להעביר את התוכן של הפונקציה כארגומנט של הפונקציה.

SyntaxError: missing } after function body

זו גם שגיאה פשוטה יחסית - היא אומרת בעיקרון ששכחנו אחת מהסוגריים המסולסלות במשפט תנאי שרשמנו. 

SyntaxError: expected expression, got 'string' or SyntaxError: unterminated string literal

שגיאות אלו אומרות באופן כללי ששכחנו להוסיף סימן של ' או ". בשגיאה הראשונה string יוחלף עם תו לא ידוע שהדפדפן ימצא במקום המרכאות או הגרש. השגיאה השניה אומרת שהמחרוזת לא הסתיימה עם גרש או מרכאות.

לתשומת לב: ראה גם SyntaxError: Unexpected token וגם SyntaxError: unterminated string literal  - דפי ההסבר שלנו לפרטים נוספים על שגיאות אלו.

לסיכום

כעת אנו יודעים את הבסיס להבין שגיאות בתוכניות JavaScript הפשוטות שלנו. רק צריך לזכור שזה לא יהיה תמיד קל להבין מה השתבש לנו בקוד. כאשר שגיאה מתרחשת, הסתכלו על מספר השורה שאתם מקבלים, ולכו לאותה שורה על מנת לנסות לאתר מה השתבש. זכרו תמיד שהשגיאה לא תמיד תהיה בשורה זו, ושאולי היא לא תהיה אחת מאלו שעברנו עליהם במאמר זה.

ראה גם

במודול זה