אחסון מידע היה מתבצע בג'אווהסקריפט באופן קבוע באובייקטים או במערכים של אובייקטים. לג'אווהסקריפט, עד 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 מאפשרת לזכרון להשתחרר אם אובייקטים שנכנסים לתוכה נמחקים.
2 תגובות
מאמר מצוין, תודה.
למה אתה לא משתמש בדוגמאות בlet?
patzatz
רק בשביל זה שווה להתקין את בבל בNODE