Перевод статьи 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Наличие следующих символов
=>
ив одной строке поневоле вводит нас в заблуждение.
И так рассмотрим все варианты визуального выделения жирной стрелки от оператора сравнения. И первый из них заключается в переносе логического выражения в скобки:
const negativeToZero = number => (numberВторой вариант — преднамеренно определить стрелочную функцию стрелки, используя ее более длинную форму:
const negativeToZero = number => { return 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
.