Menu

Type Coercion ב-JavaScript: המרות מרומזות מול מפורשות

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

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

שני סוגים של המרה

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

  • מפורשת: אתם מבקשים אותה: Number("42"), String(99), Boolean(value).
  • מרומזת: אופרטור מפעיל אותה בלי שכתבתם המרה: "5" - 2, "" == 0, if (value).

שתיהן מגיעות בסוף לאותם חוקי המרה בסיסיים. ההבדל הוא אם אתם או השפה החלטתם להמיר. כמעט כל תוצאה מבלבלת ב-JavaScript, כמו "5" + 1 === "51", [] == false או null == undefined, נובעת מהמרה מרומזת שתפסה אתכם לא מוכנים.

המודל המחשבתי: אם אתם כותבים Number(...) או String(...), אתם יודעים מה אתם מבקשים. אם לא, לכל אופרטור יש דעה משלו על מה לעשות, ובדעות האלה גרים הבאגים.

שלושת טיפוסי היעד

המרה תמיד מתבצעת לאחד משלושה טיפוסים פרימיטיביים: string, number או boolean. (יש רביעי, bigint, אבל ערכים מטיפוסים אחרים אף פעם לא מומרים אליו אוטומטית.) כל שאר החוקים נגזרים מהשאלה לאיזה יעד האופרטור מכוון.

המרה למחרוזת היא הסלחנית מכולן: לכל ערך יש צורה של מחרוזת. שימו לב שאובייקטים הופכים למחרוזת חסרת התועלת "[object Object]", ולכן הדפסה של אובייקט משורשר למחרוזת ("user: " + user) כמעט אף פעם לא מראה את מה שרציתם. השתמשו במקום זאת ב-JSON.stringify או ב-template literals עם שדות ספציפיים.

המרה למספר

המרה למספר קפדנית יותר. מחרוזות חייבות באמת להיראות כמו מספר, אחרת מקבלים NaN:

ההפתעות שמסומנות בסוגריים הן אלה ששווה לזכור:

  • מחרוזת ריקה ומחרוזות שיש בהן רק רווחים הופכות ל-0, לא ל-NaN.
  • null הופך ל-0, אבל undefined הופך ל-NaN.
  • מערך ריק הופך ל-0; מערך עם איבר אחד מומר לפי אותו איבר; מערך עם כמה איברים הופך ל-NaN.

אם רוצים לחלץ מספר ממחרוזת שיש בה תווים נוספים, השתמשו ב-parseInt או ב-parseFloat במקום ב-Number:

העבירו תמיד את הבסיס (10) ל-parseInt. בלעדיו, מחרוזות שמתחילות ב-"0x" מפוענחות כהקסדצימליות, ולעיתים רחוקות זה מה שהתכוונתם.

המרה לבוליאני

המרה לבוליאני היא הפשוטה מבין השלוש. רשימה קצרה של ערכים מומרת ל-false, וכל השאר true.

ערכי ה-falsy הם:

  • false
  • 0, -0, 0n
  • "" (מחרוזת ריקה)
  • null
  • undefined
  • NaN

זהו. כל ערך אחר, כולל "false", "0", [] ו-{}, הוא truthy.

התוצאות של מערך ריק ואובייקט ריק תופסות אנשים שמגיעים מ-Python, שבה אוספים ריקים הם falsy. ב-JavaScript הם truthy: אם אתם רוצים לדעת "האם המערך הזה ריק?", בדקו במפורש arr.length === 0.

המרה לבוליאני מתבצעת בכל פעם שערך מופיע במקום שבו מצופה בוליאני: if (...), while (...), האופרטור הטרנרי ? : והאופרטורים הלוגיים &&, ||, !.

האופרטור + הוא מקרה מיוחד

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

החישוב מתבצע משמאל לימין. 1 + 2 + "3" מחשב קודם 1 + 2 = 3, ואז 3 + "3" = "33". אבל "1" + 2 + 3 מתחיל כמחרוזת ונשאר מחרוזת: "12", ואז "123".

זו הסיבה שבניית מחרוזות עם + שבירה. ל-template literals אין את הבעיה הזו:

ה-template literal מחשב את count + 1 כביטוי מספרי נפרד, ורק אז משבץ את התוצאה. בלי המרה מפתיעה.

העדיפו המרות מפורשות

כשאתם צריכים להמיר טיפוס, אמרו את זה. זה עולה כמה תווים ומסיר כל עמימות עבור הקורא הבא:

אותו דבר עם בוליאנים. !!value עובד ונפוץ, אבל Boolean(value) אומר בדיוק מה הוא עושה:

כלל סביר: השתמשו בהמרות מפורשות בלוגיקה של האפליקציה, ושמרו את הצורות הקצרות (+x, !!x) למקומות שבהם הקיצור חשוב והכוונה ברורה מההקשר.

האופרטור == נשען בכבדות על המרה

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

כל true למעלה הוא תוצאה של שרשרת המרות רב שלבית שרוב המפתחים לא יכולים לשחזר מהזיכרון. זו הבעיה: קוד שעובד במקרה הוא קוד שיישבר בהמשך. ניכנס לכל חוקי השוויון בעמוד הבא; בינתיים, השורה התחתונה היא: השתמשו ב-=== כברירת מחדל, ותשתמשו ב-== רק בביטוי הספציפי x == null (שתופס גם null וגם undefined).

לחבר את הכול יחד

דוגמה מלאה שמראה איפה המרה עוזרת ואיפה היא מזיקה:

שימו לב לקריאה השנייה. Number("") מחזיר 0, לא NaN, ולכן parsePrice("") מחזיר 0, מה שכנראה לא היה רוצה מי שמשתמש בפונקציה הזו. אם צריך לדחות קלט ריק, הוסיפו בדיקה מפורשת:

לדעת אילו ערכים מומרים ל-0 ואילו ל-NaN זה בדיוק מה שמציל אתכם מבאג עדין בהמשך הדרך.

מה לקחת מכאן

  • המרה מתבצעת למחרוזת, למספר או לבוליאני, בהתאם לאופרטור.
  • + עם מחרוזת כלשהי הופך לשרשור; כל אופרטור אריתמטי אחר ממיר למספר.
  • ערכי falsy הם רשימה קצרה וקבועה, שננו אותה. כל השאר truthy, כולל [] ו-{}.
  • Number("") הוא 0, Number([]) הוא 0, Number(null) הוא 0, אבל Number(undefined) הוא NaN. אלה מופיעים בבאגים אמיתיים.
  • העדיפו Number(x), String(x), Boolean(x) על פני טריקים מרומזים מתוחכמים. העצמי העתידי שלכם יודה לכם.

הבא בתור: אופרטורי שוויון

כל מה שהמרה עושה בהשוואות גר בתוך ==. העמוד הבא עובר על == מול === מול Object.is, על הביטוי היחיד שבו == עדיין שימושי, ועל הסיבה ש-linters מסמנים כברירת מחדל את הצורה הרופפת.

שאלות נפוצות

מה זה type coercion ב-JavaScript?

Type coercion הוא המרה אוטומטית של ערך מטיפוס אחד לאחר ש-JavaScript מבצעת: מספר שהופך למחרוזת, מחרוזת שהופכת למספר, או כל ערך שהופך לבוליאני. זה קורה באופן מרומז כשאופרטורים כמו +, == או if פוגשים ערך שאינו מהטיפוס שהם מצפים לו, ובאופן מפורש כשאתם קוראים בעצמכם ל-Number(x), String(x) או Boolean(x).

מה ההבדל בין המרה מרומזת להמרה מפורשת?

המרה מפורשת היא כשקוראים לפונקציית המרה בכוונה: Number("42"), String(99), Boolean(value). המרה מרומזת היא כשאופרטור מפעיל המרה מאחורי הגב שלכם: "5" - 2 נותן 3, "5" + 2 נותן "52". המרה מפורשת קריאה וצפויה; המרה מרומזת היא המקור לרוב הבאגים מסוג "למה זה NaN".

איך ממירים מחרוזת למספר ב-JavaScript?

השתמשו ב-Number("42") להמרה קפדנית (מחזיר NaN אם המחרוזת אינה מספר תקין), או ב-parseInt("42px", 10) / parseFloat("3.14em") אם רוצים לקרוא מספר מתחילתה של מחרוזת מבולגנת יותר. האופרטור האונרי + (+"42") עושה אותו דבר כמו Number(), אבל קל יותר לפספס אותו כשמרפרפים על הקוד.

למה [] + [] מחזיר מחרוזת ריקה?

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

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

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

להתחיל