רכיב סמלי בסיסי מייצג ערך ייחודי שלא מתנגש אף פעם עם ערך אחר, כולל הערך של רכיבים סמלים בסיסיים אחרים. שתי מחרוזות פרימיטיביות המורכבות מתווים זהים נחשבות לזהות לחלוטין:
String() === String()
> true
String( "My string." ) === String( "My string." );
> true
עם זאת, אף שני סמלים שנוצרו באמצעות הפונקציה Symbol()
לא יכולים להיות זהים לחלוטין:
Symbol() === Symbol()
> false
המאפיין הזה מאפשר להשתמש בסמלים כמפתחות נכסים ייחודיים באובייקט, וכך למנוע התנגשויות עם מפתחות שקוד אחר עשוי להוסיף לאובייקט.
const mySymbol = Symbol( "Desc" );
const myObject = {};
myObject[mySymbol] = "propSymbol";
myObject
> Object { Symbol("Desc"): "propSymbol" }
יש שלושה סוגים של סמלים:
- סמלים שנוצרו באמצעות
Symbol()
- סמלים משותפים שמוגדרים ומאוחזרים מרשם סמלים גלובלי באמצעות
Symbol.for()
- 'סמלים מוכרים' מוגדרים כמאפיינים סטטיים באובייקט Symbol. הסמלים האלה מכילים שיטות פנימיות שאי אפשר להחליף בטעות.
Symbol()
מקבלת תיאור (או 'שם סמל') כארגומנט אופציונלי.
התיאורים האלה הם תוויות שקריאות לבני אדם למטרות ניפוי באגים, והן לא משפיעות על הייחודיות של התוצאה. כל קריאה ל-Symbol
מחזירה פרימיטיב של סמל ייחודי לחלוטין, גם אם לקריאות מרובות יש תיאורים זהים:
Symbol( "My symbol." ) === Symbol( "My symbol." );
> false
בדומה לסוגים אחרים של נתונים פרימיטיביים, סמלים יורשים שיטות ומאפיינים מאב הטיפוס שלהם. לדוגמה, אפשר לגשת לתיאור כנכס שעובר בירושה מהסמל שנוצר:
let mySymbol = Symbol( "My symbol." );
mySymbol.description
> "My symbol."
אבל אי אפשר ליצור סמל באמצעות מילת המפתח new
:
let mySymbol = new Symbol();
> Uncaught TypeError: Symbol is not a constructor
אי אפשר למנות סמלים, כלומר אי אפשר להשתמש במאפיינים סמליים כשמשתמשים בשיטות רגילות כדי לבצע איטרציה עליהם. השיטה getOwnPropertySymbols()
מאפשרת גישה למאפייני הסמל של אובייקט.
סמלים משותפים
השיטה Symbol.for()
מנסה לחפש סמלים קיימים במרשם סמלים גלובלי ברמת זמן הריצה, עם מחרוזת נתונה כמפתח, ומחזירה את הסמל התואם אם נמצא כזה. אם לא נמצא סמל כזה, נוצר סמל עם המפתח שצוין ומתווסף למרשם הגלובלי:
let sharedSymbol = Symbol.for( "My key." );
sharedSymbol === Symbol.for( "My key." )
> true
אין חפיפה פונקציונלית בין המפתחות האלה לבין התיאורים שהוקצו לפרימיטיבים מסוג Symbol
שנוצרו על ידי המחבר. כדי לגשת לסמל במרשם הסמלים, קודם צריך ליצור אותו באמצעות for()
:
Symbol( "String" ) === Symbol( "String" );
> false
Symbol( "String" ) === Symbol.for( "String" );
> false
Symbol.for( "String" ) === Symbol.for( "String" );
> true
כדי לאחזר את המפתח של כל סמל מרשם הסמלים, משתמשים ב-Symbol.keyFor()
:
let mySymbol = Symbol.for( "Key." );
Symbol.keyFor( mySymbol ) ;
> "Key."
סמלים 'מוכרים'
סמלים מוכרים הם מאפיינים סטטיים של האובייקט Symbol
, שכל אחד מהם הוא סמל בעצמו. סמלים מוכרים מספקים מפתחות נכסים ייחודיים לגישה לשיטות המובנות של JavaScript ולשינוי שלהן, תוך מניעת החלפה לא מכוונת של התנהגות הליבה.
Symbol;
> function Symbol()
asyncIterator: Symbol(Symbol.asyncIterator)
for: function for()
hasInstance: Symbol("Symbol.hasInstance")
isConcatSpreadable: Symbol("Symbol.isConcatSpreadable")
iterator: Symbol(Symbol.iterator)
keyFor: function keyFor()
length: 0
match: Symbol("Symbol.match")
matchAll: Symbol("Symbol.matchAll")
name: "Symbol"
prototype: Object { … }
replace: Symbol("Symbol.replace")
search: Symbol("Symbol.search")
species: Symbol("Symbol.species")
split: Symbol("Symbol.split")
toPrimitive: Symbol("Symbol.toPrimitive")
toStringTag: Symbol("Symbol.toStringTag")
unscopables: Symbol("Symbol.unscopables")
<prototype>: function ()
מאחר שסמלים הם תכונה ספציפית ל-ES6, הערכים הסמלים האלה מיועדים לשמש כ'נקודות הרחבה' למפתחים שמשנים את ההתנהגות של JavaScript בלי ליצור בעיות בתאימות לאחור.
לעיתים קרובות, ערכים של סמלים מוכרים מופיעים בסגנון מיוחד עם קידומת @@
או עטופים ב-%
כדי להבדיל בין המפתחות שלהם לבין האב טיפוס שלהם שאפשר לשנות. לדוגמה, @@match
(או %match%
) הוא הפניה ל-Symbol.match
הבלתי משתנה, ולא ל-String.prototype.match
.
בדיקת ההבנה
אפשר להשתמש ב-new
כדי ליצור סמל?
אילו מהסמלים הבאים מתארים סמלים 'ידועים'?