Перевод статьи 5 Best Practices to Write Quality Arrow Functions.

Стрелочные функции заслуженно пользуются популярностью. Их синтаксис лаконичен, связан лексически, отлично подходят использования в качестве функций обратного вызова.

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

1. Вывод имен стрелочных функций

Стрелочные функции в JavaScript являются анонимными, поэтому свойство name такой функции является, по сути, пустой строкой ''.

( number => number + 1 ).name; // => ''

Во время сеанса отладки и анализа стека вызовов в Chrome DevTools анонимные функции помечаются как anonymous. К сожалению эта подсказка anonymous не дает никаких дополнительных указаний о ходе выполнения нашего кода.

На рисунке ниже вы можете видеть окно Chrome DevTools с сеансом отладки выполнения кода двух анонимных функций:

С правой стороны окна находится стек вызовов, в котором находятся две функции, помеченные как анонимные anonymous. Как видим мы не можем получить ничего полезного из представленной, в таком виде, информации о текущем стеке вызовов.

К счастью, существует такой способ как function name inference (вывод имен функций), являющийся особенностью стандарта ES2015, который поможет нам задать имя нашим функциям. Идея возможности вывода имени функции состоит в том, что JavaScript может определять имя стрелочной функции по ее синтаксической позиции: например, из имени переменной, которая содержит объект функции.

Давайте посмотрим, как работает такой вывод имени функции:

const increaseNumber = number => number + 1;

increaseNumber.name; // => 'increaseNumber'

Поскольку переменная increaseNumber содержит стрелочную функцию, то JavaScript определяет, что increaseNumber может стать отличным именем для этой функции. Таким образом, стрелочная функция получает имя IncreNumber.

Хорошей практикой является использование function name inference для именования стрелочных функций в вашем коде.

Теперь давайте запустим сеанс отладки с кодом, который использует function name inference:

Поскольку теперь у стрелочных функций назначены имена, то стек вызовов дает нам гораздо больше информации о выполняемом коде:

  • имя функции handleButtonClick указывает на то, что произошло событие click ;
  • IncreaseCounter увеличивает переменную счетчика.

2. Inline, когда это возможно

Inline функция — это функция, которая содержит только одно выражение. И мне очень нравится в стрелочных функциях эта возможность написания коротких inline функций.

Например, вместо использования длинной формы следующей стрелочной функции:

const array = [1, 2, 3];

array.map((number) => { 
  return number * 2;
});

Вы можете легко удалить фигурные скобки {} и оператор return, если стрелочная функция имеет одно выражение (инструкцию):

const array = [1, 2, 3];

array.map(number => number * 2);

Вот мой совет:

Если ваша функция будет содержать только одно выражение, то хорошей практикой является использовать ее в виде inline-функции с более короткой формой записи.

3. Символ жирной стрелки и операторы сравнения

Следующие логические операторы сравнения >, <, <= и >= схожи с жирной стрелкой =>, которая определяет стрелочную функцию. И если эти операторы используются в inline стрелочной функции, то это создает некоторую путаницу.

Давайте определим функцию стрелки, которая использует оператор <=:

const negativeToZero = number => number <= 0 ? 0 : number;

Наличие следующих символов => и <= в одной строке поневоле вводит нас в заблуждение.

И так рассмотрим все варианты визуального выделения жирной стрелки от оператора сравнения. И первый из них заключается в переносе логического выражения в скобки:

const negativeToZero = number => (number <= 0 ? 0 : number);

Второй вариант — преднамеренно определить стрелочную функцию стрелки, используя ее более длинную форму:

const negativeToZero = number => {
  return number <= 0 ? 0 : number;
};

Рассмотренные нами способы устраняют путаницу между обозначением жирной стрелки и логическими операторами сравнения.

Если стрелочная функция содержит логические операторы >, <, <= и >=, то хорошей практикой является перенос выражения в скобки или намеренное использование более длинной формы определения стрелочной функции.

4. Построение простых объектов

Использование объектного литерала внутри inline стрелочной функции вызывает синтаксическую ошибку:

const array = [1, 2, 3];

// выдает SyntaxError!
array.map(number => { 'number': number });

Интерпретатор JavaScript считает фигурные скобки блоком кода, а не литералом объекта.

Оборачивание литерала объекта в пару скобок решает проблему:

const array = [1, 2, 3];

// Работает!
array.map(number => ({ 'number': number }));

Если литерал объекта содержит много свойств, то вы можете даже использовать перевод строки, сохраняя при этом стрелочной функции вид inline:

const array = [1, 2, 3];

// Работает!
array.map(number => ({
  'number': number
  'propA': 'value A',
  'propB': 'value B'
}));

И так моя вот моя рекомендация на этот случай:

Оберните литералы объекта в пару круглых скобок при использовании внутри стрелочных inline функций.

5. Разбираемся с чрезмерной вложенностью

Синтаксис стрелочной функции достаточно короткий, и это хорошо. Но, как побочный эффект, он может быть не совсем понятным, если несколько стрелочных функций вложены друг в друга.

Давайте рассмотрим следующий пример. При нажатии кнопки отправляется запрос к серверу. Когда ответ готов (принят и корректно прочитан), то содержимое его элементов выводится в консоль:

myButton.addEventListener('click', () => {
  fetch('/items.json')
    .then(response => response.json());
    .then(json => {
      json.forEach(item => {
        console.log(item.name);
      });
    });
});

В представленном примере кода стрелочные функции имеют 3 уровня вложенности. И требуется затратить определенные усилия и время для того, чтобы понять, что именно делает этот код.

Для того, чтобы повысить удобство чтения и понимание вложенных функций, используется подход, который заключается во введении переменных, каждой из которой присваивается соответствующая стрелочная функция. Переменная должна кратко описывать то, что делает функция (см. рекомендацию о выводе имени стрелочной функции).

const readItemsJson = json => {
  json.forEach(item => console.log(item.name));
};

const handleButtonClick = () => {
  fetch('/items.json')
    .then(response => response.json());
    .then(readItemsJson);
};

myButton.addEventListener('click', handleButtonClick);

В ходе рефакторинга передаем стрелочные функции в переменные readItemsJson и handleButtonClick. Уровень вложений уменьшается с 3 до 2. Теперь стало проще понять, что делает наш скрипт.

Еще лучше, если бы мы переписали всю функцию с использованием синтаксиса async/await, что является отличным способом решения проблемы излишней вложенности функций:

const handleButtonClick = async () => {
  const response = await fetch('/items.json');
  const json = await response.json();
  json.forEach(item => console.log(item.name));
};

myButton.addEventListener('click', handleButtonClick);

Резюмируем:

Хорошая практика — избегать чрезмерного вложения стрелочных функций, передавая их в переменные как отдельные функции, а если это возможно, использовать синтаксис async / await.

6. Выводы

Стрелочные функции в JavaScript являются анонимными. Чтобы сделать отладку кода, содержащего функции этого типа, наиболее продуктивной, рекомендуется использовать переменные для хранения соответствующих стрелочных функций. Это позволяет JavaScript выводить имена функций в Chrome DevTools.

inline стрелочные функции удобны, когда тело функции содержит одно выражение.

Логические операторы >, <, <= и >= выглядят как символ жирная стрелка =>. Поэтому необходимо соблюдать некоторую осторожность, когда они используются внутри стрелочных inline функций.

Синтаксис литерала объекта {prop: 'value'} аналогичен коду блока {}. Поэтому, когда литерал объекта помещается внутри inline стрелочной функции, то во избежание ошибок необходимо заключить его в пару круглых скобок: () => ({prop: 'value'}).

Наконец, чрезмерное вложение функций скрывает смысл использования нашего кода. Хорошим подходом к сокращению вложенности стрелочных функций — передача их соответствующим переменным. В качестве альтернативы, попробуйте использовать синтаксис async / await.

Оставить комментарий