[Перевод] 5 рекомендаций по написанию качественных стрелочных функций

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

hh3xqnisixnavkhxuijsmwvauq0.png

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

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


Стрелочные функции в JavaScript анонимны: их свойство name содержит пустую строку — ''.

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


Анонимные функции маркируются как anonymous в процессе отладки или при анализе стека вызовов. К сожалению, слово anonymous не содержит в себе подсказки о том, к какому конкретно коду оно имеет отношение.

Вот один из моментов отладки кода, в котором вызываются анонимные функции.

539f9d30809526c5f3ab7cfb3920e22b.png


Отладка кода, содержащего анонимные функции

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

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

Посмотрим на механизм выведения имён функций в действии:

const increaseNumber = number => number + 1;

increaseNumber.name; // => 'increaseNumber'


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

Рекомендуется использовать механизм выведения имён функций для именования стрелочных функций.

Теперь посмотрим на процесс отладки, в ходе которого используется механизм выведения имён функций.

e26c72329b91ac90a6e649b1bb124c5f.png


Отладка кода, содержащего стрелочные функции, которым даны имена

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

  • Имя функции 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.

Уважаемые читатели! Знаете какие-нибудь интересные приёмы использования стрелочных функций?

1ba550d25e8846ce8805de564da6aa63.png

© Habrahabr.ru