פעם אחת ולתמיד: מה הבעיה עם new Array
פוסט זה כולל טיפ קצר בנושא פיתוח Front End. אם אתם רוצים ללמוד יותר לעומק על פיתוח Front End מהבסיס ועד הנושאים המתקדמים תשמחו לשמוע שבניתי קורס וידאו מקיף בנושא זה הכולל מעל 50 שיעורי וידאו והמון תרגול מעשי.
למידע נוסף והצטרפות לקורס בקרו בדף קורס Front End באתר.
הבנאי new Array עם פרמטר מספרי תמיד מצליח להפתיע, מאחר והוא מייצר מערך שלא הייתם יכולים להגיע אליו עם אופרטור הסוגריים המרובעים. בואו נדבר על ציפיות, הדפסות ואיך מתמודדים.
1. התוצאה הצפויה
הכנסו לקונסול והריצו את הקוד הבא. או אם אתם מתעצלים הנה מה שהקונסול בכרום שלי מציג:
x = new Array(5)
[undefined × 5]
x.length
5
x[0]
undefined
x[1]
undefined
עד לפה נראה טוב. עושה רושם שקיבלנו מערך של 5 תאים ובכולם הערך undefined. אבל המציאות קצת יותר מורכבת.
2. כמה הפתעות
המשיכו עם הפקודות הבאות:
x.forEach((item) => console.log('*'))
undefined
מה זה? אם היה לנו ביד מערך עם 5 תאים ריקים היינו מצפים לקבל 5 הדפסות של *. במקום קיבלנו undefined, כלומר הלולאה לא רצה. באותו האופן הקוד הבא לא מצליח לייצר מערך של 5 מספרים אקראיים:
new Array(5).map(() => Math.random())
זה מפתיע כי אם ניצור אם המערך בעצמנו עם סוגריים מרובעים נקבל התנהגות שונה:
[undefined, undefined, undefined, undefined, undefined].map(() => Math.random())
[0.5062684734916629, 0.49865581015958504, 0.5796565300881666, 0.053705448191098126, 0.032301133236884993]
3. מערכים ב JavaScript
הסיבה לבלבול היא היכולת של JavaScript לשמור מערכים שלא תואמים לגודל שלהם, או בשפה מקצועית מערכים דלילים (Sparse Arrays). נסו את הקוד הבא:
x = [];
x[1000] = 10;
x.length;
// 1001
גודל המערך לאחר ההשמה הוא 1001, למרות שאין במערך 1001 איברים אלא איבר יחיד. אלף האיברים שלפניו הם ״חורים״ עליהם JavaScript דילג. היתרון במערכים כאלה הוא חסכון בזכרון, שהרי אין צורך להקצות זכרון עבור אלף איברים שאינם קיימים.
החסרון בשיטה שהיא יוצרת בלבול במושג האורך. המאפיין length אינו מחזיר את מספר האיברים במערך אלא אחד יותר מהאינדקס של האיבר האחרון. במערכים דלילים מספר זה גדול ממש ממספר האיברים.
הפונקציות map ו forEach מבצעות איטרציה על איברים אמיתיים בלבד, ולכן מדלגות על ״חורים״ שקיימים במערכים הדלילים. הבנאי new Array שמקבל מספר יוצר מערך דליל שיש לו רק אורך אך לא איברים.
ומה בנוגע למערך של מספרים אקראיים? האם לנצח נצטרך את underscore בשביל ליצור אותו? כלל לא. הפונקציה fill מקבלת ערך מילוי וכותבת אותו לכל החורים במערך, כך שהקוד הבא מחזיר את אותו מערך של 5 מספרים אקראיים שראינו:
new Array(5).fill(5).map(() => Math.random())
[0.47725481802697267, 0.923667050149505, 0.9437440002801853, 0.38107224135324436, 0.29496275372480674]