ES6 - Arrow Functions

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

var people = [
  { name: 'Dana', age: 32 },
  { name: 'Yossi', age: 19 },
  { name: 'Dorit', age: 8 },
]

var grownUps = people.filter(function(p) {
  return p.age > 18;
});

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

Arrow Functions - מה השתנה?

Arrow Functions קצרות בהרבה מפונקציות ״רגילות״ וכתוצאה מכך מאפשרות לנו לכתוב פונקציות קצרות, בשורה אחת ועם מטרה אחת.
וכמובן שאפשר לכתוב פונקציות עם מטרה אחת ללא Arrow Functions, אך העובדה שצורת הכתיבה של הפונקציה כבר לא ״עמוסה״, גורמת, לדעתי, לקצר את אורך הפונקציה ולחשוב עליה מלכתחילה כפונקציה עם מטרה יחידה.

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

var grownUps = people.filter(p => p.age > 18);

כאשר אנו כותבים פונקציות קצרות אשר מקבלות פרמטר אחד, כוללות ביטוי אחד ומחזירות ערך כלשהוא, צורת הכתיבה תהיה פרמטר => ביטוי. אין צורך ב- function, בסוגריים מסולסלות וב- return לפני הביטוי.

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

// ES5
setTimeout(function() {
    alert('Suprise!');
  }, 100);

// ES6
setTimeout(() => alert('Suprise!'), 100);

וכך נכתוב פונקציה שמועברים אליה מספר פרמטרים:

var numbers = [1, 2, 3];

// ES5
numbers.reduce(function(a, b) {
  return a + b;
});

// ES6
numbers.reduce((a, b) => a + b);

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

// ES5
button.addEventListener('click', function(event) {
  event.stopPropagation();
  buttonClicked = true;
});

// ES6
button.addEventListener('click', event => {
  event.stopPropagation();
  buttonClicked = true;
});

מה זה זה?

נוסף לשינוי צורת הכתיבה של Arrow Functions מפונקציות אנונימיות רגילות, ישנו שינוי התנהגות משמעותי.

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

function Counter() {
  this.seconds = 0;

  setInterval(function() { // New anonymous function
    this.seconds++; // What's seconds? S:
    console.log(this.seconds); // NaN
  }, 1000);
}

var myCounter = new Counter();

Arrow Functions, לעומת זאת, אינם מבדילים את עצמם מהתחום בו הם נמצאים ואינם יוצרים ערך this חדש.
כתוצאה מכך, אין צורך יותר לחשוב על מה יהיה הערך של this כשנשתמש בפונקציה, ואפשר לכתוב פונקציות כמו שהיינו כותבים משפטי if-else או switch מבלי לחשוש על ערך ה- this בתוכם.

function Counter() {
  this.seconds = 0;

  setInterval(() => { // New anonymous function
    this.seconds++;
    console.log(this.seconds); // 1.. 2.. 3..
  }, 1000);
}

var myCounter = new Counter();

Arrow Functions חוסכים המון כאב ראש כשזה מגיע לקריאת קוד והתמודדות עם this, אך הן לא מתאימות לכל מקרה - מאחר שהן פונקציות ללא שם (אנונימיות), לא ניתן להשתמש בהן שוב לאחר כתיבתן, ולא ניתן לבצע איתן פעולות רקורסיביות.

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