Menu

Optional Chaining (?.) ב-JavaScript: גישה בטוחה למאפיינים מקוננים

איך האופרטור ?. מאפשר לכם להגיע לתוך אובייקטים, מערכים ומתודות מקוננים בלי לקרוס על null או undefined.

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

הבעיה: גישה מקוננת שבירה

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

user.address הוא undefined, וקריאה של .city מתוך undefined זורקת TypeError: Cannot read properties of undefined. נתונים בעולם האמיתי, תגובות API, JSON מפוענח, שאילתות DOM, מלאים בשדות שאולי קיימים ואולי לא, וכתיבת בדיקות הגנה לכל רמה נהיית רועשת מהר מאוד:

const city = user && user.address && user.address.city;

קריא פחות או יותר עם שתי רמות. כואב עם ארבע. האופרטור ?. הוא הפתרון הנקי יותר.

?. עושה Short-Circuit על null ו-undefined

Optional chaining קורא מאפיין רק אם הערך שלפניו אינו null או undefined. אם כן, כל הביטוי מחושב ל-undefined ונעצר.

השורה הראשונה קוראת את name כרגיל. השורה השנייה מגיעה ל-user.address, רואה undefined ונסוגה: שאר השרשרת לא רצה אף פעם. השורה השלישית הייתה זורקת שגיאה בלי ?., כי .toUpperCase() הייתה נקראת על undefined; עם ?., כל הביטוי מחושב בשקט ל-undefined.

המודל המנטלי: ?. אומר "אם מה שלפניי הוא null או undefined, וותר והחזר undefined, אחרת המשך".

זה עובד גם על מערכים וקריאות לפונקציות

שלושה תחבירים, אותו רעיון:

  • ?.[...] לגישה למערך או למאפיין דינמי.
  • ?.() לקריאה למשהו שאולי פונקציה ואולי לא.
  • ?.name לגישה רגילה למאפיין.

שלושתם עושים short-circuit באותו אופן. user?.notAMethod?.() לא זורק שגיאה למרות ש-notAMethod לא קיים: ה-?. השני רואה undefined ונעצר.

זה שימושי במיוחד ל-callbacks אופציונליים:

אין יותר צורך בשומרי if (typeof onDone === "function") לפני כל קריאה.

רק null ו-undefined מפעילים את ה-Short-Circuit

זה הפרט שמפיל אנשים. ?. מתעניין רק בערכי nullish. ערכי falsy אחרים, 0, "", false, NaN, הם ערכים תקינים לגמרי להמשיך מהם את השרשרת (לפחות במידה ש-autoboxing מאפשר):

data.count הוא 0, שהוא falsy אבל לא nullish, ולכן ?.toFixed(2) רץ עליו ומחזיר "0.00". השוו את זה ל-&&:

גרסת ה-&& מחזירה 0, כי data.count הוא falsy ועושה short-circuit. גרסת ה-?. מחזירה "0.00", כי 0 אינו nullish. אם אתם באמת רוצים לעצור על 0, && נכון. אם אתם רוצים לעצור רק כש"הערך חסר", ?. הוא מה שאתם צריכים.

המיקום של ה-? חשוב

?. מגן על הערך שלפניו, לא על זה שאחריו. לכן שימו אותו ברמה שעלולה להיות חסרה:

כאן שלושתם עובדים, כי שום דבר לא באמת חסר. אבל אם config.server עלול להיות undefined, אתם צריכים config.server?.host: ?. לפני server לא יעזור, כי הבעיה היא קריאה של .host מתוך server חסר.

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

השמה דרך ?. לא עובדת

Optional chaining מיועד לקריאה בלבד. אי אפשר להשתמש בו בצד השמאלי של השמה:

user?.address?.city = "Paris";   // SyntaxError

זה הגיוני אם חושבים על זה: מה המשמעות של השמה למאפיין של undefined? אם צריך להציב ערך רק כשההורה קיים, כתבו את זה במפורש:

מתי להשתמש בו, ומתי לא

?. מצטיין כשערך הוא אופציונלי באמת:

  • תגובות API שבהן שדות אולי כלולים ואולי לא.
  • חיפושים ב-DOM: document.querySelector(".banner")?.remove().
  • Callbacks שאולי לא יועברו: options.onError?.(err).
  • קריאות משורשרות לספריות שבהן תוצאות ביניים יכולות להיות null.

זה הכלי הלא נכון כשהערך אמור להתקיים תמיד. פיזור ?. כדי להשתיק שגיאות במקרה כזה הופך באג רועש שקל למצוא ("TypeError on line 42") לבאג שקט (משתנה שמשום מה הוא undefined שלוש פונקציות משם). כשמשהו חייב להיות שם, תנו לו לזרוק שגיאה: ה-stack trace עושה לכם טובה.

דוגמה מציאותית

שליפת ערך מתוך תגובת API שאולי חלקית:

כל ?. מטפל ברמה אחת של אי-ודאות. avatarUrl מסתיים כ-undefined בצורה נקייה. onClick?.() קורא ל-handler אם הוא במקרה קיים.

שימו לב ל-?? בשורה של displayName: זה החצי השני של הדפוס הזה. ?. נותן לכם undefined כשמשהו חסר; ?? מאפשר להציב ברירת מחדל בלי למעוד על ערכי falsy לגיטימיים כמו 0 או "".

הבא בתור: Nullish Coalescing

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

שאלות נפוצות

מה עושה ?. ב-JavaScript?

האופרטור ?. ניגש למאפיין, לאינדקס של מערך או למתודה רק אם הערך שלפניו אינו null או undefined. אם כן, כל הביטוי עושה short-circuit ומחושב ל-undefined במקום לזרוק שגיאה. user?.address?.city לא יקרוס כש-user או address חסרים.

מתי כדאי להשתמש ב-optional chaining ב-JavaScript?

השתמשו ב-?. כשערך רשאי באמת להיות חסר: תגובות API עם שדות אופציונליים, חיפושים ב-DOM שאולי לא ימצאו צומת, callbacks שאולי יועברו ואולי לא. אל תשתמשו בו כדי להסתיר באגים במקומות שבהם ערך אמור להתקיים תמיד; במקרים האלה ערך חסר הוא מידע אמיתי שאתם רוצים לראות.

מה ההבדל בין ?. לבין &&?

ברוב המקרים הם מפיקים את אותה תוצאה, אבל ?. עושה short-circuit רק על null או undefined, בעוד ש-&& עושה short-circuit על כל ערך falsy, כולל 0, '' ו-false. obj && obj.count מחזיר 0 כש-count הוא 0; גם obj?.count מחזיר 0, אבל השרשרת נעצרת בצורה נקייה רק על ערכי nullish.

אפשר להשתמש ב-optional chaining עם מערכים ועם קריאות לפונקציות?

כן. arr?.[0] קורא אינדקס של מערך בבטחה, ו-fn?.() קורא לפונקציה רק אם היא קיימת. שניהם פועלים לפי אותו כלל: אם הערך שלפני ?. הוא null או undefined, הביטוי הוא undefined ושום דבר נוסף לא רץ.

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

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

להתחיל