Menu

Higher-Order Functions ב-JavaScript: map, filter, reduce ו-Callbacks

פונקציות מסדר גבוה ב-JavaScript: העברת פונקציות כארגומנטים, החזרת פונקציות מפונקציות אחרות, ותבניות map/filter/reduce שתשתמשו בהן כל יום.

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

פונקציות הן ערכים

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

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

קבלת פונקציה כארגומנט

הצורה הנפוצה ביותר: פונקציה שמקבלת callback ומריצה אותו בשבילכם. כבר השתמשתם בכאלה בלי לחשוב על זה.

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

כתיבה של פונקציה כזאת בעצמכם נראית אותו דבר. הנה פונקציה קטנה שמריצה callback רק אם תנאי מתקיים:

action הוא פרמטר שבמקרה מחזיק פונקציה. קריאה לו עם action() מריצה את מה שהועבר.

שלוש שבאמת תשתמשו בהן: map, filter, reduce

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

map: שינוי של כל איבר

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

filter: שמירה של מה שמתאים

filter שומרת את האיברים שעבורם ה-callback מחזיר ערך truthy ומשליכה את השאר. ערך ההחזרה הוא מערך חדש, אולי קצר יותר.

reduce: קיפול המערך לערך אחד

reduce היא הכללית מכולן. ה-callback מקבל את הצובר (accumulator) הנוכחי ואת האיבר הנוכחי, ומה שתחזירו הופך לצובר הבא. הארגומנט השני (0 כאן) הוא ערך ההתחלה.

אפשר לשרשר אותן. כאן הסגנון הזה משתלם באמת:

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

החזרת פונקציה מפונקציה

החצי השני של מסדר גבוה: פונקציה שבונה ומחזירה פונקציה אחרת:

multiplyBy(2) רצה פעם אחת ומחזירה פונקציה חדשה לגמרי. הפונקציה החדשה עדיין זוכרת את factor: זה closure, ויש לו עמוד משלו בהמשך. בינתיים, המסקנה: קריאה ל-multiplyBy עם ארגומנטים שונים נותנת פונקציות ייעודיות שונות שנבנו מאותה תבנית.

התבנית הזאת מופיעה בכל מקום:

הגדרה אחת, שתי פונקציות לשימוש חוזר. עדיף בהרבה על כתיבת warn ו-info ידנית ושמירה על סנכרון ביניהן.

פונקציות עם שם מול callbacks בשורה

אפשר להעביר arrow function בתוך השורה, או להעביר פונקציה לפי השם שלה. שתי הדרכים עובדות, בחרו את מה שנקרא טוב יותר:

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

nums.filter(isEven);     // correct: passes the function
nums.filter(isEven());   // wrong: calls isEven with no args, passes the result

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

דוגמה מלאה

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

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

מתי לא להשתמש בהן

מתודות מסדר גבוה מצוינות, אבל הן לא תחליף ללולאה בכל מצב:

  • אם צריך לעצור באמצע, for או for...of עם break ברורים יותר מניסיון לצאת מ-forEach.
  • אם אתם עושים משהו אסינכרוני בתוך ה-callback, map ו-forEach לא יחכו לו. השתמשו ב-for...of עם await, או ב-Promise.all עם map.
  • אם ה-callback משנה state משותף, אתם מתרחקים מהחוזקות של הסגנון הזה. השתמשו בלולאה רגילה או שכתבו את הקוד כך שיחזיר ערכים חדשים.

במקומות שמתאימים, map, filter ו-reduce חותכות את רוב קוד הלולאות החוזר מהקוד היומיומי. בכל מקום, הן מתחילות לפגוע בקריאות. בחרו את הכלי שהופך את הכוונה לברורה ביותר.

הבא בתור: אובייקטים

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

שאלות נפוצות

מה זו higher-order function ב-JavaScript?

פונקציה מסדר גבוה היא פונקציה שעושה לפחות אחד משני דברים: מקבלת פונקציה אחרת כארגומנט, או מחזירה פונקציה כתוצאה. Array.prototype.map, setTimeout ו-addEventListener הן כולן מסדר גבוה: הן מקבלות callback ומריצות אותו בשבילכם.

מה ההבדל בין map, filter ו-reduce?

map משנה כל איבר ומחזירה מערך חדש באותו אורך. filter שומרת את האיברים שעבורם ה-callback מחזיר ערך truthy ומחזירה מערך (שאולי קצר יותר). reduce מקפלת את המערך לערך יחיד על ידי שילוב האיברים אחד אחרי השני. שלושתן פונקציות מסדר גבוה שמקבלות callback.

למה להחזיר פונקציה מפונקציה אחרת?

כדי לבנות פונקציות עזר קטנות שניתנות להגדרה בלי לחזור על לוגיקה. פונקציה כמו multiplyBy(n) מחזירה פונקציה חדשה שמכפילה ב-n, כך ש-multiplyBy(2) ו-multiplyBy(10) נותנות שתי פונקציות ייעודיות מהגדרה אחת. התבנית הזאת משתמשת ב-closures ונפוצה ב-event handlers, ב-middleware ובספריות עזר.

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

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

להתחיל