Стрелочные функции были впервые представлены в ES6 (ES2015) в 2015 году.

Несмотря на то, что они выглядят чище, по сравнению с обычными функциями есть некоторые отличия, которые могут сбить вас с толку, если вы их не понимаете.

Вот руководство по использованию стрелочных функций.

Прежде всего давайте сравним стрелочную функцию с обычной функцией:

// ES6 Arrow function
const hello1 = () => {console.log('Hello world')}
// ES5 Function
const hello2 = function() {
    console.log('Hello world')
}
hello1(); // Hello world
hello2(); // Hello world

Первое очевидное отличие заключается в том, что нам не нужно писать слово function, а второе заключается в том, что мы используем => для перемещения в тело функции. Выглядит лучше, думаю, многие согласятся.

Больше никаких return или скобок

Есть также случаи, когда нам больше не нужно ключевое слово return или круглые скобки. Рассмотрим следующий пример. У нас есть массив лет, и мы хотим узнать, как давно произошло событие.

const years = [1970, 1984, 1945, 1998, 2010];
const yearsAgoES5 = years.map(function(year) {
    return 2020 - year
});
const yearsAgoES6 = years.map(year => 2020 - year);
console.log(yearsAgoES5); // [ 50, 36, 75, 22, 10 ]
console.log(yearsAgoES6); // [ 50, 36, 75, 22, 10 ]

Тот же результат, меньше кода.

Нам больше не нужно использовать ключевое слово return для возврата количества лет или использовать круглые скобки вокруг year. Однако вам потребуются круглые скобки, если у нас будет более одного аргумента, такого как index, к которому карта также дает нам доступ.

Мы также могли бы использовать фигурные скобки, если бы захотели, и если бы мы это сделали, нам снова потребовалось бы ключевое слово return:

const yearsAgoES6 = years.map((year, index) => {
    return `${index}: ${2020 - year}`
});

Лексическая область действия (это)

Одно из основных различий между функциями ES5 и стрелочными функциями ES6 заключается в том, что стрелочная функция разделяет ключевое слово this со своим окружением, что является их лексической областью действия.

Однако функции ES5 используют значения на основе их контекста, то есть объекта, который их вызывает.

Давайте рассмотрим пример ES5, где у нас есть объект со свойством функции, который хочет получить доступ к другим свойствам объекта.

const objES5 = {
  name: 'Sam Orgill',
  job: 'Web developer',
  website: 'samorgill.com',
  print: function() {
    console.log(this.name); // Sam Orgill
    setTimeout(function() {
      console.log(this.name); // undefined
    }, 1000);
  }
}
objES5.print();

Мы видим, что когда вызывается печать function, она правильно выходит из системы Sam Orgill. Это связано с тем, что при вызове функции print() она разделяет контекст объекта.

Когда мы входим в функцию setTimeout(), она больше не использует контекст объекта objES5, вместо этого она использует контекст глобального объекта window в вашем браузере.

Однако почти всегда бывает так, что когда вы писали подобный код, вы имели в виду ссылку на objES5.name, поэтому я на самом деле считаю это ошибкой в ES5, хотя другие с этим не согласятся.

Одним из распространенных способов обойти это было сохранение this в новой переменной с именем self в функции print, а затем использование ее в setTimeout, чтобы она могла получить доступ к контексту объектов.

const objES5 = {
  name: 'Sam Orgill',
  job: 'Web developer',
  website: 'samorgill.com',
  print: function() {
    var self = this;
    setTimeout(function() {
      console.log(self.name); // Sam Orgill
    }, 1000);
  }
}
objES5.print();

Стрелочные функции и this

Стрелочные функции в ES6 разделяют лексическое значение this своего окружения (objES6). Это означает, что все в пределах objES6 находится в пределах его лексической области. Поэтому, если мы преобразуем функцию setTimeout() в стрелочную функцию, мы можем свободно ссылаться на objES6.name.

const objES6 = {
  name: 'Sam Orgill',
  job: 'Web developer',
  website: 'samorgill.com',
  print: function() {
    console.log(this.name); // Sam Orgill
    setTimeout(() => {
      console.log(this.name); // Sam Orgill
    }, 1000);
  }
}
objES6.print();

Когда не следует использовать функцию стрелки

А как насчет нашей функции print? Можем ли мы также преобразовать это в функцию стрелки? Конечно, мы можем. Но это сломается.

const objES6 = {
  name: 'Sam Orgill',
  job: 'Web developer',
  website: 'samorgill.com',
  print: () => {
    console.log(this.name); // undefined
    setTimeout(() => {
      console.log(this.name); // undefined
    }, 1000);
  }
}
objES6.print();

Причина, по которой этот пример выходит из системы undefined, заключается в том, что функция стрелки принимает значение this своего окружения, которое в случае первого console.log было бы глобальным объектом window, а не objES6, и поэтому второй console.log также разделяет глобальный объект window, который, конечно же, не содержит нашего name.

Хотите поднять свои навыки веб-разработки на новый уровень?

Я создал курс Основы Angular, предназначенный для людей, не имеющих опыта работы с Angular. Скидка 90% сейчас 🚀