Menu

שדות פרטיים ב-JavaScript: התחביר # לפרטיות במחלקות

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

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

הבעיה עם קו תחתון

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

_count נראה פרטי אבל הוא לא. כל קוד שקורא למחלקה יכול לקרוא אותו, לכתוב אליו או למחוק אותו. הקו התחתון הוא שלט מנומס על הדלת; הדלת עצמה פתוחה לרווחה.

JavaScript מודרנית פתרה את זה עם שדות פרטיים אמיתיים, שמסומנים ב-#.

ה-# הופך אותו לפרטי

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

מתוך המחלקה, this.#count עובד כרגיל. מבחוץ, הוא פשוט לא קיים:

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

מתודות ו-getters פרטיים

לא רק שדות אפשר לסמן כפרטיים. מתודות, getters ו-setters מקבלים כולם את הקידומת #:

#assertPositive היא פונקציית עזר פנימית. היא לא חלק מה-API הציבורי, ולכן כשהיא פרטית באמת אף אחד לא יקרא לה בטעות מבחוץ, ואף אחד גם לא יכול להסתמך עליה, כך שאתם חופשיים לשנות לה את השם או להסיר אותה בהמשך.

איברים סטטיים פרטיים

גם איברים סטטיים יכולים להיות פרטיים. מוסיפים להם # באותה צורה:

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

מחלקות יורשות לא רואות אותם

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

אין protected ב-JavaScript. אם מחלקה יורשת צריכה את הנתונים, מחלקת האב חייבת לחשוף מתודה, getter או (לעיתים רחוקות יותר) שדה שאינו פרטי. העיצוב הזה מכוון: פרטי זה באמת פרטי, והירושה לא פורצת בו חורים.

בדיקת שדה פרטי עם in

לפעמים רוצים לוודא שאובייקט באמת שייך למחלקה שלכם, מה שנקרא בדיקת מותג (brand check). האופרטור in עובד עם שמות של שדות פרטיים בתוך המחלקה:

מכיוון שרק Wallet יכולה ליצור אובייקטים עם #balance, הביטוי #balance in obj הוא בדיקה אמינה לכך ש-obj הוא מופע אמיתי של Wallet. במקרי קצה מסוימים זה מהיר ובטוח יותר מ-instanceof, כי אי אפשר לזייף שדות פרטיים מבחוץ.

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

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

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

מתי להשתמש ב-#

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

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

השתמשו במאפיינים ציבוריים כשמשהו הוא באמת חלק מהממשק. השתמשו ב-getter (get name()) כשאתם רוצים גישת קריאה בלבד לשדה פרטי. וותרו על מוסכמת הקו התחתון: היא הייתה פתרון עוקף לחוסר שהשפה כבר השלימה.

#celsius הוא האחסון הנסתר; celsius ו-fahrenheit הם תצוגות לקריאה בלבד. מי שמשתמש במחלקה לא יכול לקלקל את המצב הפנימי, והמחלקה חופשית לשנות בהמשך את האופן שבו היא שומרת את הערך.

הבא בתור: Prototypes

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

שאלות נפוצות

איך מצהירים על שדה פרטי ב-JavaScript?

מוסיפים לשם השדה את הקידומת #, גם בהצהרה וגם בכל גישה אליו. class Counter { #count = 0; increment() { this.#count++; } } יוצר שדה פרטי באמת. ה-# הוא חלק מהשם, לא אופרטור.

מה ההבדל בין #field ל-_field ב-JavaScript?

_field הוא רק מוסכמת שמות: המאפיין עדיין ציבורי וכל אחד יכול לקרוא אותו או לכתוב אליו. #field נאכף על ידי השפה: קוד מחוץ למחלקה פשוט לא יכול לגשת אליו, וניסיון לעשות זאת זורק SyntaxError כבר בשלב הניתוח. השתמשו ב-# כשאתם רוצים פרטיות אמיתית.

האם מחלקות יורשות יכולות לגשת לשדות פרטיים של מחלקת האב?

לא. שדות פרטיים מוגבלים למחלקה שמצהירה עליהם, ואפילו מחלקות יורשות לא יכולות לגעת בהם. אם מחלקה יורשת צריכה גישה, מחלקת האב חייבת לחשוף מתודה או getter. זה מחמיר יותר מ-protected בשפות אחרות, וזה מכוון.

אפשר לבדוק אם לאובייקט יש שדה פרטי?

כן, עם האופרטור in בתוך המחלקה: #field in obj מחזיר true אם ל-obj יש את השדה הפרטי הזה. זה שימושי לבדיקות מותג (brand checks): לוודא שאובייקט הוא באמת מופע של המחלקה שלכם לפני שקוראים למתודות שלו.

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

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

להתחיל