Menu

תחביר ונקודה-פסיק ב-JavaScript: חוקי ASI בהסבר פשוט

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

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

קוד JavaScript הוא רצף של הוראות

תוכנית JavaScript היא רשימה של הוראות (statements): פקודות שהמנוע מריץ מלמעלה למטה. הצהרה על משתנה היא הוראה. קריאה לפונקציה היא הוראה. בלוק if או for הוא הוראה. ההוראות מופרדות זו מזו בנקודה-פסיק.

שלוש הוראות, שלוש נקודות-פסיק. המנוע מריץ את הראשונה, אחר כך את השנייה ואז את השלישית. פשוט למדי.

לרווחים ולהזחות אין שום משמעות עבור ה-parser. אפשר לשים את שלושתן בשורה אחת עם נקודה-פסיק ביניהן, והקוד ירוץ בדיוק אותו דבר. אנחנו מזיחים בשביל קוראים אנושיים, לא בשביל המנוע.

בלוקים מקבצים הוראות יחד

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

שתי הקריאות ל-console.log בתוך הסוגריים הן הגוף של ה-if. שימו לב שאין נקודה-פסיק אחרי ה-} הסוגר: בלוקים הם לא הוראות שצריך לסיים. הוראת ה-if כולה מסתיימת כשהבלוק מסתיים.

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

ביטויים מול הוראות

הבחנה אחת ששווה להבין מוקדם: ביטויים (expressions) מפיקים ערך, הוראות עושות משהו.

  • 2 + 3 הוא ביטוי. הערך שלו הוא 5.
  • let x = 2 + 3; היא הוראה. היא מצהירה על משתנה ומציבה בו את התוצאה של ביטוי.
  • console.log("hi") הוא ביטוי (קריאה לפונקציה מחזירה undefined), אבל כשהוא כתוב בשורה משלו עם נקודה-פסיק הוא הופך ל_הוראת ביטוי_ (expression statement).

ברוב הזמן לא צריך לחשוב על זה. זה חשוב כשנתקלים ב-arrow functions, באופרטור טרנרי ובמקומות שבהם JavaScript רוצה ביטוי אבל ניסיתם לתת לה הוראה (או להפך).

שאלת הנקודה-פסיק

הנה התשובה הכנה: ל-JavaScript יש יכולת שנקראת Automatic Semicolon Insertion (ASI), שמכניסה בשבילכם נקודות-פסיק חסרות ברוב ירידות השורה. כלומר, קוד כזה רץ בלי בעיה:

בלי נקודה-פסיק, בלי שגיאה. ASI מסתכל על כל ירידת שורה ושואל: "האם הטוקן הבא יכול להמשיך את ההוראה הנוכחית?" אם לא, הוא מכניס נקודה-פסיק.

בגלל ASI, יש בשטח שני סגנונות לגיטימיים:

  • תמיד נקודה-פסיק. רוב בסיסי הקוד, רוב המדריכים, רוב מדריכי הסגנון.
  • בלי נקודה-פסיק. בשימוש בחלק מהפרויקטים המודרניים (סגנון Standard, חלק מבסיסי הקוד של React). מסתמך על ASI ועל כמה טריקים הגנתיים.

שניהם עובדים. אף אחד מהם לא "שגוי". מה ש_כן_ שגוי הוא חוסר עקביות: ערבוב סגנונות בתוך קובץ אחד מקשה לזהות באגים.

איפה ASI נוקם בכם

ASI עושה את הדבר הנכון בכ-99% מהמקרים. ה-1% הנותר הוא שורות ש_מתחילות_ בטוקן שיכול להמשיך את השורה הקודמת. עושי הצרות הם [, (, `, +, - ו-/.

הסתכלו על זה:

הייתם מצפים ש-x ו-y יתחלפו. במקום זה, ASI לא מכניס נקודה-פסיק לפני [x, y], כי 10[x, y] תקין מבחינה תחבירית (גישה לפי אינדקס למספר 10). ה-parser קורא את שורה 2 ואת שורה 3 כביטוי אחד גדול, ומקבלים שגיאת זמן ריצה או פלט חסר משמעות.

הפתרון הוא נקודה-פסיק בסוף שורה 2:

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

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

המלכודת של return

מקרה ASI אחד שווה לזכור בעל פה, כי הוא יוצר באגים שקטים. אם שמים ירידת שורה מיד אחרי return, ASI מכניס שם נקודה-פסיק, והפונקציה מחזירה undefined:

הכותב התכוון להחזיר אובייקט. ASI ראה return בסוף שורה וסיים את ההוראה במקום. ה-object literal הפך לקוד שאי אפשר להגיע אליו.

הפתרון הוא להתחיל את הערך באותה שורה של return:

אותו כלל חל על throw, break, continue ו-yield: אל תשימו ירידת שורה בין מילת המפתח לערך שלה.

כלל פשוט שעובד

אם אתם רק מתחילים, הדרך הקלה ביותר היא:

  1. כתבו נקודה-פסיק במפורש בסוף כל הוראה.
  2. אל תשימו אותה אחרי } שסוגר בלוק של if, for, while, הצהרות על פונקציות או גופים של מחלקות.
  3. כן שימו אותה אחרי } של object literal או של ביטוי פונקציה שמוצב במשתנה: const f = function() {};.
  4. השתמשו בכלי עיצוב (Prettier, ESLint עם כלל סגנון) והפסיקו לחשוב על זה. הכלי אוכף את מה שהצוות שלכם החליט.

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

רגישות לאותיות גדולות ומזהים

שני כללים קטנים שגם הם שייכים לכאן:

  • JavaScript רגישה לאותיות גדולות וקטנות. userName, username ו-UserName הם שלושה מזהים שונים.
  • מזהים יכולים להכיל אותיות, ספרות, _ ו-$, אבל לא יכולים להתחיל בספרה. הם גם לא יכולים להיות מילים שמורות כמו class, return או function.

ה-$ חוקי, אבל לפי המוסכמה הוא בדרך כלל שמור לדברים שספציפיים לספרייה מסוימת (jQuery השתמשה בו בעבר, וחלק ממערכות התבניות עדיין משתמשות). לעיתים רחוקות תבחרו בו בעצמכם.

הבא בתור: Strict Mode

JavaScript מודרני רץ בשקט בגרסה מחמירה יותר של השפה שנקראת strict mode, שהופכת כמה התנהגויות מרושלות לשגיאות אמיתיות. מודולי ES וגופים של מחלקות הם strict כברירת מחדל, אבל שווה לדעת מה זה משנה בפועל, וזה העמוד הבא.

שאלות נפוצות

האם צריך נקודה-פסיק ב-JavaScript?

מבחינה טכנית לא: ל-JavaScript יש Automatic Semicolon Insertion (ASI), שמוסיף אותן בשבילכם ברוב ירידות השורה. בפועל, רוב הצוותים עדיין כותבים אותן במפורש, כי ל-ASI יש כמה מקרי קצה (שורות שמתחילות ב-[, (, `, +, -, /) שבהם הוא עושה את הדבר הלא נכון. בחרו סגנון ותנו לכלי עיצוב כמו Prettier לאכוף אותו.

מה זה הכנסה אוטומטית של נקודה-פסיק ב-JavaScript?

ASI הוא הכלל שמאפשר ל-JavaScript להכניס נקודה-פסיק חסרה בסוף שורה כשהטוקן הבא לא יכול להמשיך את ההוראה הנוכחית. בזכותו let x = 1 עובד בלי נקודה-פסיק. הוא נכשל כששורה מתחילה במקרה במשהו ש_כן_ יכול להמשיך את השורה הקודמת, כמו [ או (, ו-JavaScript מדביקה בשקט את שתי השורות יחד.

מה התחביר הבסיסי של הוראה ב-JavaScript?

הוראה (statement) היא פקודה: הצהרה על משתנה, קריאה לפונקציה, בלוק if, לולאה. הוראות מופרדות בנקודה-פסיק (מפורשת או כזו שהוכנסה אוטומטית). בלוקים של הוראות נכתבים בתוך { }. ה-parser מתעלם מרווחים ומהזחות, כך שהעיצוב הוא בשביל בני אדם, לא בשביל המנוע.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל