[Перевод] 5 рекомендаций по написанию качественных стрелочных функций
Стрелочные функции в JavaScript довольно популярны. И они этого заслуживают, отличаясь лаконичным синтаксисом, лексической привязкой this
и тем, что их очень удобно использовать в качестве коллбэков.
Материал, перевод которого мы сегодня публикуем, включает в себя 5 рекомендаций по использованию стрелочных функций. У того, кто воспользуется этими рекомендациями, есть шанс полнее раскрыть потенциал таких функций.
1. Пользуйтесь выводом имён стрелочных функций
Стрелочные функции в JavaScript анонимны: их свойство name
содержит пустую строку — ''
.
( number => number + 1 ).name; // => ''
Анонимные функции маркируются как anonymous
в процессе отладки или при анализе стека вызовов. К сожалению, слово anonymous
не содержит в себе подсказки о том, к какому конкретно коду оно имеет отношение.
Вот один из моментов отладки кода, в котором вызываются анонимные функции.
Отладка кода, содержащего анонимные функции
Стек вызовов в правой части рисунка содержит упоминания о 2 функциях, отмеченных как anonymous
. Из подобной информации, содержащейся в стеке вызовов, нельзя узнать о коде ничего полезного.
К счастью, в JavaScript имеется механизм выведения имён функций (возможность ES2015), который способен, в определённых условиях, обнаруживать имя функции. Идея вывода имени заключается в том, что JavaScript может выяснить имя стрелочной функции из её синтаксической позиции, то есть — на основании имени переменной, хранящей ссылку на объект функции.
Посмотрим на механизм выведения имён функций в действии:
const increaseNumber = number => number + 1;
increaseNumber.name; // => 'increaseNumber'
Так как ссылка на стрелочную функцию хранится в переменной increaseNumber
, JavaScript решает, что имя 'increaseNumber'
хорошо подойдёт для функции. В результате стрелочная функция и получает такое имя.
Рекомендуется использовать механизм выведения имён функций для именования стрелочных функций.
Теперь посмотрим на процесс отладки, в ходе которого используется механизм выведения имён функций.
Отладка кода, содержащего стрелочные функции, которым даны имена
Так как теперь у стрелочных функций есть имена, стек вызовов даёт больше полезных сведений о выполняемом коде:
- Имя функции
handleButtonClick
указывает на вызов обработчика событияclick
. - Имя
increaseCounter
указывает на вызов функции, увеличивающей счётчик.
2. Всегда, когда это возможно, стремитесь писать встраиваемые функции
«Встраиваемая» функция — это функция, состоящая из одного выражения. В стрелочных функциях мне нравится то, что их очень удобно использовать для создания коротких встраиваемых функций.
Например, вот «полная» форма стрелочной функции:
const array = [1, 2, 3];
array.map((number) => {
return number * 2;
});
Эту функцию легко можно сделать компактнее. А именно, так как функция содержит всего одно выражение, можно убрать фигурные скобки и return
:
const array = [1, 2, 3];
array.map(number => number * 2);
Рекомендовано, если функция содержит в себе всего одно выражение, делать её встраиваемой.
3. Осторожно используйте стрелочные функции и операторы сравнения
Операторы сравнения >
, <
, <=
и >=
очень похожи на стрелку =>
, с помощью которой объявляют стрелочные функции (такие стрелки ещё называют «жирными стрелками»).
Когда операторы сравнения используются во встраиваемой функции, получающийся код может оказаться не очень понятным.
Объявим стрелочную функцию, в которой используется оператор <=
:
const negativeToZero = number => number <= 0 ? 0 : number;
Наличие в одной строке символов =>
и <=
дезориентирует читателя кода.
Для того чтобы чётко отличать стрелки от операторов сравнения, можно воспользоваться несколькими подходами.
Во-первых, можно заключить выражение в круглые скобки:
const negativeToZero = number => (number <= 0 ? 0 : number);
Во-вторых, можно намеренно объявить стрелочную функцию, воспользовавшись более длинной конструкцией:
const negativeToZero = number => {
return number <= 0 ? 0 : number;
};
Эти преобразования снимают ту неопределённость, которую вызывает использование в одной строке и символа стрелки и операторов сравнения.
Рекомендуется, если однострочная стрелочная функция содержит операторы >
, <
, <=
и >=
, заключать соответствующие выражения в круглые скобки или пользоваться многострочной формой объявления стрелочной функции.
4. Используйте скобки или многострочные конструкции при создании простых объектов в стрелочных функциях
Использование объектного литерала внутри встраиваемой стрелочной функции приведёт к появлению синтаксической ошибки:
const array = [1, 2, 3];
// выбрасывает SyntaxError!
array.map(number => { 'number': number });
JavaScript считает фигурные скобки блоком кода, а не объектным литералом.
Если же заключить объектный литерал в круглые скобки — эта проблема будет решена:
const array = [1, 2, 3];
// Работает!
array.map(number => ({ 'number': number }));
Если в объектном литерале имеется много свойств, то тут даже можно использовать переводы строк. Стрелочная функция при этом всё ещё остаётся встраиваемой:
const array = [1, 2, 3];
// Работает!
array.map(number => ({
'number': number
'propA': 'value A',
'propB': 'value B'
}));
Объектные литералы, при использовании их во встраиваемых функциях, рекомендуется заключать в круглые скобки.
5. Внимательно относитесь к слишком глубоким вложенным конструкциям
Стрелочные функции отличаются лаконичным синтаксисом. Это хорошо. Но из-за этого много вложенных друг в друга стрелочных функций могут образовывать трудночитаемые конструкции.
Рассмотрим следующий сценарий. Когда нажимают на кнопку — выполняется запрос к серверу. Когда получают ответ сервера, содержащий набор неких элементов, имена этих элементов выводятся в консоль:
myButton.addEventListener('click', () => {
fetch('/items.json')
.then(response => response.json());
.then(json => {
json.forEach(item => {
console.log(item.name);
});
});
});
Здесь мы видим три уровня вложенности стрелочных функций. Для того чтобы вникнуть в то, что происходит в этом коде, нужно потратить некоторое время.
Для улучшения читабельности вложенных функций можно воспользоваться несколькими подходами.
Первый подход заключается в том, чтобы записать ссылки на функции в переменные. Имена переменных должны кратко описывать сущность функции (взгляните на рекомендацию №1, посвящённую выводу имён функций).
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
.
Итоги
Стрелочные функции в JavaScript анонимны. Для того чтобы сделать отладку кода более продуктивной, рекомендуется использовать переменные, хранящие ссылки на функции. Это позволяет JavaScript выводить имена функций.
Встраиваемые стрелочные функции удобны в тех случаях, когда тело функции содержит лишь одно выражение.
Операторы >
, <
, <=
и >=
похожи на стрелку =>
, используемую при объявлении стрелочных функций. Когда эти операторы используются в теле встраиваемой стрелочной функции, стоит задуматься о преобразовании кода.
Синтаксис объектных литералов, наподобие { prop: 'value' }
, похож на блок кода {}
. В результате, когда объектный литерал размещается внутри встраиваемой стрелочной функции, его нужно заключить в круглые скобки: () => ({ prop: 'value' })
.
Слишком большие уровни вложенности функций запутывают того, кто читает код. Рекомендуется уменьшать уровень вложенности функций, извлекая их и записывая в переменные. Ещё один подход к уменьшению уровня вложенности кода заключается в применении конструкции async/await
.
Уважаемые читатели! Знаете какие-нибудь интересные приёмы использования стрелочных функций?