שבעה פרימיטיבים וכל השאר
JavaScript מחלקת ערכים לשני מחנות. בצד אחד, שבעה טיפוסים פרימיטיביים: ערכים פשוטים שלא ניתן לשנות. בצד השני, אובייקטים: כל מה שמורכב, ניתן לשינוי או ניתן לקריאה. זו כל מערכת הטיפוסים ברמת הערכים.
שבעת הפרימיטיבים:
כל מה שלא ברשימה הזו, מערכים, פונקציות, תאריכים, ביטויים רגולריים ו-{} רגיל, הוא אובייקט. typeof נותן את הטיפוס בזמן ריצה, ותשימו לב לפגם המפורסם בשורה האחרונה. typeof null מחזיר 'object' מאז 1995 ולעולם לא יתוקן; יותר מדי קוד קיים מסתמך עליו.
פרימיטיב הוא ערך, לא מכל
המודל המחשבתי שהכי עוזר: פרימיטיב הוא הערך שלו. המספר 3 הוא לא קופסה שמחזיקה 3, הוא פשוט 3. שני משתנים שמחזיקים 3 מחזיקים את אותו ערך, ולא שני עותקים שמצביעים על משהו משותף:
פרימיטיבים מושווים לפי ערך. אובייקטים מושווים לפי הפניה. ההבחנה הזו לבדה עומדת מאחורי הרבה רגעים של "למה זה false?" בהמשך, במיוחד כשמשווים מערכים או אובייקטים עם ===.
פרימיטיבים אינם ניתנים לשינוי
אי אפשר לשנות פרימיטיב. כל פעולה שנראית כמו שינוי בעצם מייצרת ערך חדש:
הקריאה הראשונה בונה מחרוזת חדשה וזורקת אותה, כי שום דבר לא תפס את הערך המוחזר. השנייה מבצעת השמה מחדש ל-name. ה-"ada" המקורי מעולם לא השתנה, והוא גם לא יכול היה להשתנות. אותו דבר עם מספרים: x + 1 מייצר מספר חדש ולא משנה את x.
זו הסיבה ש-const על מחרוזת או מספר הוא בטוח באמת. הערך לא יכול להשתנות, ו-const מונע השמה מחדש למשתנה.
Number, BigInt, ולמה יש שניים
ה-number של JavaScript הוא מספר נקודה צפה של 64 ביט. זה אומר חישובים מהירים ותקרה: מספרים שלמים מדויקים רק עד Number.MAX_SAFE_INTEGER (2^53 - 1):
מעבר לסף הזה, מספרים שלמים מתחילים להתנגש. bigint קיים בשביל מספרים שלמים שצריכים להישאר מדויקים בכל גודל. כותבים אותו עם הסיומת n:
אי אפשר לערבב bigint ו-number בחישובים, כי כך הייתם מאבדים את כל הטעם של הדיוק הנוסף. השתמשו ב-bigint כשאתם עובדים עם מזהים ממסד נתונים, חותמות זמן בננו-שניות או קריפטוגרפיה. חישובים רגילים נשארים עם number.
גם מחרוזות הן פרימיטיבים
מחרוזת ב-JavaScript היא פרימיטיב, לא אובייקט, למרות שיש לה מתודות כמו .length, .slice ו-.toUpperCase:
מאחורי הקלעים, כשקוראים למתודה על מחרוזת, JavaScript עוטפת אותה לרגע באובייקט String כדי שקריאת המתודה תעבוד, ואז זורקת את העטיפה. לא צריך לחשוב על העטיפה הזו, רק לדעת שמחרוזות מתנהגות כמו ערכים (לא ניתנות לשינוי ומושוות לפי ערך) למרות שיש להן אוסף עשיר של מתודות.
גרשיים בודדים, גרשיים כפולים ו-backticks יוצרים כולם את אותו טיפוס. backticks מאפשרים בנוסף שילוב ערכים ומחרוזות מרובות שורות, והדף הבא מסביר אותם.
null מול undefined
שני פרימיטיבים אומרים "אין ערך", והם לא ניתנים להחלפה זה בזה.
undefined הוא מה שמקבלים כשמשהו מעולם לא קיבל ערך: משתנה שהוצהר ולא הוגדר, ארגומנט חסר לפונקציה, מאפיין שלא קיים:
null הוא מה ש_אתם_ כותבים כשאתם רוצים לומר "ריק בכוונה":
המוסכמה הכללית: undefined הוא הדרך של השפה לומר "אין כאן כלום", null הוא הדרך של המתכנת. שניהם falsy, שניהם נכשלים בהשוואה לערכים רגילים, ולכל אחד מהם יש דף משלו בהמשך.
Symbol: ייחודי מעצם הגדרתו
symbol הוא הפרימיטיב שהכי פחות משתמשים בו. כל symbol שיוצרים הוא ייחודי, גם אם שניים נבנו מאותו תיאור:
symbols שימושיים כמפתחות של אובייקטים שלא יכולים להתנגש במפתחות קיימים: ספרייה יכולה להצמיד מטא-דאטה לאובייקטים שלכם עם symbol ולהיות בטוחה ששום קוד אחר לא ידרוס אותה. תפגשו אותם שוב סביב איטרטורים ו-symbols מוכרים כמו Symbol.iterator.
בדיקת טיפוסים בזמן ריצה
typeof מטפל ברוב המקרים. זכרו את המוזרויות האלה:
עבור null, השוו ישירות: value === null. עבור מערכים, השתמשו ב-Array.isArray(value). לשאלה "האם זה פרימיטיב כלשהו?" אין פונקציה מובנית אחת, אבל הביטוי המקובל ברור מספיק:
פרימיטיבים מול אובייקטים: המלכודת של השמה
עוד דבר אחד ששווה לראות לפני שממשיכים. מכיוון שפרימיטיבים הם ערכים ואובייקטים הם הפניות, השמה מתנהגת אחרת:
עם פרימיטיבים, b = a מעתיק את הערך. עם אובייקטים, y = x מעתיק את ההפניה: שני השמות מצביעים על אותו אובייקט. שינוי דרך שם אחד משפיע על השני. זה המקור הגדול ביותר לבאגים מסוג "רגע, למה זה השתנה?" ב-JavaScript.
מה לקחת מכאן
- שבעה פרימיטיבים:
string,number,bigint,boolean,null,undefined,symbol. כל השאר הוא אובייקט. - פרימיטיבים אינם ניתנים לשינוי ומושווים לפי ערך; אובייקטים ניתנים לשינוי ומושווים לפי הפניה.
typeofמגלה את הטיפוס בזמן ריצה, עם שתי מוזרויות ששווה לזכור:typeof null === "object"ו-typeof function === "function".numberהוא מספר נקודה צפה של 64 ביט עם תקרת בטיחות למספרים שלמים;bigintקיים בשביל מספרים שלמים מדויקים מעבר לתקרה הזו.
הבא בתור: מחרוזות ו-Template Literals
מחרוזות הן הפרימיטיב שתעבדו איתו הכי הרבה, ו-template literals (המחרוזות עם backticks) הופכים את בנייתן לקלה: שילוב ערכים, טקסט מרובה שורות ו-tagged templates. זה הדף הבא.
שאלות נפוצות
כמה טיפוסים פרימיטיביים יש ב-JavaScript?
שבעה: string, number, bigint, boolean, null, undefined ו-symbol. כל השאר, מערכים, פונקציות, תאריכים ואובייקטים רגילים, הוא אובייקט. אפשר לבדוק את הטיפוס של ערך בזמן ריצה עם typeof, עם מוזרות היסטורית אחת: typeof null מחזיר 'object'.
מה ההבדל בין פרימיטיב לאובייקט ב-JavaScript?
פרימיטיבים הם ערכים שלא ניתן לשנות, והם מושווים לפי ערך: שני 3 הם אותו 3. אובייקטים ניתנים לשינוי ומושווים לפי הפניה: שני {} שונים זה מזה גם אם הם נראים זהים. השמה של פרימיטיב מעתיקה את הערך; השמה של אובייקט מעתיקה את ההפניה לאותם נתונים.
האם פרימיטיבים ב-JavaScript באמת בלתי ניתנים לשינוי?
כן. אי אפשר לשנות פרימיטיב במקום: 'hello'.toUpperCase() מחזיר מחרוזת חדשה ולא משנה את המקורית. השמה מחדש למשתנה (x = x + 1) מחליפה את הערך שבמשתנה בפרימיטיב אחר; הערך המקורי אף פעם לא משתנה. זו הסיבה ש-const name = 'Ada' עדיין מאפשר לבנות מחרוזות חדשות מתוך name.
למה typeof null מחזיר 'object'?
זה באג במימוש המקורי משנת 1995 שמעולם לא תוקן, כי יותר מדי קוד הסתמך על ההתנהגות הזו. כדי לבדוק null, השוו עם ===: value === null. כדי לבדוק undefined, השתמשו ב-value === undefined או ב-typeof value === 'undefined'.