JavaScript

В ECMAScript2015 была добавлена стрелочная функция. Она более лаконична, чем традиционная функция, и связывает this лексически.

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

1. Добавьте имя для стрелочной функции

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

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

( number => number * 2 ).name; // ""

( number => number - 1 ).name; // ""

И тогда вы можете спросить: что же делает свойство name?

Возможно, в коде оно не понадобится, но оно пригодится, когда нужно выполнить отладку.

Предположим, что у нас был этот фрагмент кода:

let counter = 0;

setTimeout(() => {
  setInterval(() => {
    debugger;
    counter++;
  }, 1000)
}, 1000)

Вот сеанс отладки кода:

Из консоли отладки Chrome

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

Чтобы облегчить отладку, стрелочной функции необходимо добавить имя. Посмотрим, как это сделать.

Вот пример:

const increaseCounter = number => number++;

increaseCounter.name; // => 'increaseCounter'

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

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

Теперь давайте проверим сеанс отладки с помощью кода, использующего вывод имен:

let counter = 0;

const increaseCounter = () => {
  debugger;
  counter++;
}

const callback = () => {
  setInterval(increaseCounter, 1000)
}

setTimeout(callback, 1000)

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

  • callback — это функция вызова increaseCounter.
  • 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);
      });
    });
});

Стрелочные функции имеют 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.

Вывод

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

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

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

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

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

Читайте также:


Перевод статьи Tom Zhou: 5 Simple Tips to Write Better Arrow Functions

Предыдущая статьяКак сгенерировать настоящие случайные числа в Solidity с блокчейном
Следующая статьяИспользование SQLite с Rust и Actix Web (с тестами)