ECMAScript 6 – שמירת מידע ב-Map ו-Set

מבני המידע החדשים והשימושיים להפליא של es6.

אחסון מידע היה מתבצע בג'אווהסקריפט באופן קבוע באובייקטים או במערכים של אובייקטים. לג'אווהסקריפט, עד ECMAScript 6 לא היה אובייקט מסוג map (ברוב השפות מבנה שאמור לשמור מידע) ולפכיך היינו צריכים להשתמש באובייקטים לאחסנת מידע. למשל:


var o = {1: 'a', 2: 'b'};

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


Object.size = function(obj) {
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) size++;
    }
    return size;
};

// Get the size of an object
var size = Object.size(myArray);

גם יש בעיה עם המפתחות שתמיד חייבים להיות טקסט, למשל:


var o = {1: 'a', 2: 'b'};
o[1] // a
o['1'] // a

הקיצר, שימוש באובייקט כ-map הוא אפשרי אך בעייתי. תודה לאל, בדיוק בשביל זה יש את ECMAScript 6 ואובייקט map. עושים instance ל-map עם new ויש יש כמה מתודות בסיסיות: get ו-set שמשמשות להכנסה והוצאת נתונים לפי key value וכמובן את has שמחזירה בוליאני (כלומר true או false) אם יש בכלל את המפתח. הנה הדוגמאות המיוחלות:


var myMap = new Map();
    
myMap.set('foo', 123);
var a = myMap.get('foo');
console.log(a); //123
console.log(myMap.has('foo')); //true
myMap.delete('foo')
console.log(myMap.has('foo')); //false

כמובן שכל דבר (אפילו אובייקט) יכול להיות מפתח – בלי שום קשר לכך שהוא מספר או מחרוזת טקסט. למשל:


var myMap = new Map();
    
myMap.set(1, 'some value');
console.log(myMap.has(1)); //true
console.log(myMap.has('1')); //false


מתודות נוספות הן size שמחזירה את גודל ה-map ו-clear שמנקה אותו. פה אני מראה דוגמה כאשר כבר ביצירת ה-map אני מכניס איברים.


var myMap = new Map([
  [1, 'some value'],
  [2, 'another value']
  ]);
    
console.log(myMap.size); //2
myMap.clear();
console.log(myMap.size); //0

כמובן שקל לעשות איטרציות על map. ככה עושים את זה עם for of בנוגע ל-keys:


var myMap = new Map([
  [1, 'some value'],
  [2, 'another value']
  ]);
    
for (var key of myMap.keys()) {
    console.log(key);
} //1, 2

וגם עם values:


var myMap = new Map([
  [1, 'some value'],
  [2, 'another value']
  ]);
    
for (var value of myMap.values()) {
    console.log(value);
} //"some value", "another value"

חדי העין ישימו לב שהשתמשתי פה בשתי תכונות – values ו-keys שמוציאות מערך של המפתחות ושל הערכים. אפשר להוציא גם entries שהם מערך של ה-key וה-value:


var myMap = new Map([
  [1, 'some value'],
  [2, 'another value']
  ]);
    
for (var entry of myMap.entries()) {
    console.log(entry[0], entry[1]);
} //1 "some value", 2 "another value"

WeakMap

קשה להבין את WeakMap בלי להבין את עניין זליגות הזכרון של JavaScript. באופן עקרוני, WeakMapהוא בדיוק כמו map, רק שהוא מקבל רק אובייקטים כ-keys ואי אפשר לעשות לו איטרציה. כלומר יש את כל המתודות, אבל לא תצליחו לעשות לזה איטרציה. למה צריך את WeakMap בכלל? בשביל זה אני צריך להבין קצת בנושא זליגות זכרון. בואו נתאר מצב כזה – נניח שיש לי אלמנטים ב-DOM. את חלקם אני שומר ב-map מכל מיני סיבות. אם אני מוחק אותם מה-DOM. הייתי מצפה ש-JS יפנה את המקום בזכרון של האלמנטים האלו. אבל ה-map ישמור את הרפרנסים לאלמנטים האלו למרות שהם נמחקו! כי ה-garbage collector לא יבין שהוא יכול למחוק את האלמנטים האלו.
בדיוק בשביל זה יש לי את weakMap – המפתחות של ה-map הזה הם 'חלשים' ולא יכולים למנוע ממה שנכנס לתוכם להתפוגג כפלוץ ברוח. מאוד קשה להדגים את זה, אבל אני מקווה שהדוגמה הזו תהיה ברורה:

See the Pen ALrrBk by Ran Bar-Zik (@barzik) on CodePen.

בגדול – WeakMap היא כמו map, אבל עם אובייקטים בתור מפתחות ובלי size או איטרציה. אם אתם מאחסנים מידע באובייקטים ואחר כך מוחקים אותם. יש מצב ש-WeakMap יחסוך לכם הרבה צרות.

Set ו-WeakSet

כמו map, גם set הוא מבנה נתונים שלא היה עד היום בג'אווהסקריפט. בגדול התפקיד שלו זה להכיל ערכים ואנחנו יכולים לוודא אם ערך מסוים בפנים. זה שימושי לכל מיני מצבים ובעיקר ליצירת רשימות לשימוש המשכי – למשל אם יש לי רשימה של שמות שהמשתמש מוסיף ואני שולח לשרת אבל אני צריך לדאוג שאין דופליקציה, set הוא אידיאלי.


var set = new Set();
set.add('red')
console.log(set.has('red')); //true
set.delete('red')
console.log(set.has('red')); //false

אפשר גם לעשות איטרציה. אין פה מפחחות כמובן אלא רק ערכים.


var set = new Set(['red', 'green', 'blue']);
for (var x of set) {
    console.log(x); //red, green, blue
}

יש לי גם את מתודת clear לניקוי מוחלט של ה-set ותכונת size לגודל ה-set.

כמו שיש לנו את WeakMap, יש לנו גם את WeakSet שיש לה שלוש מתודות – has, set ו-delete. גם WeakSet מאפשרת לזכרון להשתחרר אם אובייקטים שנכנסים לתוכה נמחקים.

פוסטים נוספים שכדאי לקרוא

תמונה של הבית הלבן עם מחשוב ענן וטקסט: FEDRAMP
פתרונות ומאמרים על פיתוח אינטרנט

FedRAMP & FIPS מבוא למתחילים

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

מיקרו בקרים

הפעלת מסך oled קטנטן עם esp32

יצירת מערכת מולטימדיה שלמה בגודל של בול דואר עם מיקרובקר ובעלות של דולרים בודדים. וגם על הדרך נלמד על ת׳רדים

תמונה מצוירת של רובוט שמנקה HTML
יסודות בתכנות

סניטציה – למה זה חשוב

הסבר על טכניקה פשוטה וידועה מאד שאנו מפעילים על מידע לפני שאנחנו מציגים אותו ב-HTML באפליקציה או באתר.

פתרונות ומאמרים על פיתוח אינטרנט

יישום של nonce על מנת להגן מפני התקפות injection

בפוסט הקודם הסברתי על hash עם CSP על משאבי inline – שזה נחמד ומעולה אבל פחות ישים בעולם האמיתי שבו בדרך כלל התוכן ה-inline (בין

גלילה לראש העמוד