В ECMAScript2015 была добавлена стрелочная функция. Она более лаконична, чем традиционная функция, и связывает this
лексически.
Правильное использование стрелочных функций может улучшить качество кода. Однако многие программисты имеют неверное представление о них и не умеют правильно их использовать. Вот некоторые из уроков, которые я усвоил и, надеюсь, они помогут и вам.
Все традиционные функции имеют свои собственные имена, за исключением стрелочных функций.
Но такие функции в 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
увеличивает переменную счетчика.Встроенные функции — это те, которые имеют только одно выражение. Мне нравится возможность создания их в стрелочных функциях.
Например, вместо того, чтобы использовать длинную форму:
const array = [1, 2, 3];
array.map((number) => {
return number * 2;
});
Вы можете легко удалить фигурные скобки { }
и оператор return
, если стрелочная функция имеет одно выражение:
const array = [1, 2, 3];
array.map(number => number * 2);
Вот вам совет:
Когда функция имеет одно выражение, хорошей практикой является встроить стрелочную функцию.
Операторы сравнения >
,<
, <=
и >=
похожи на жирную стрелку =>
(ту, которая определяет стрелочную функцию).
Когда эти операторы используются во встроенной стрелочной функции, это создает некоторую путаницу.
Давайте определим функцию таким образом, чтобы она использовала оператор <=
:
Наличие обоих символов =>
и <=
на одной строке вводит в заблуждение.
Чтобы четко отличить жирную стрелку от оператора сравнения, можно либо обернуть выражение в пару скобок:
const negativeToZero = number => (number <= 0 ? 0 : number);Либо намеренноопределить стрелочную функцию, используя более длинную форму:
const negativeToZero = number => {
return number <= 0 ? 0 : number;
};
Такие рефакторинги устраняют путаницу между символом жирной стрелки и операторами сравнения.
Если стрелочная функция содержит операторы >
,<
, <=
и >=
, рекомендуется заключить выражение в пару скобок или намеренно использовать более длинную форму.
Литерал объекта внутри встроенной стрелочной функции вызывает синтаксическую ошибку:
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'
}));
Моя рекомендация:
Обертывайте объектные литералы в пару скобок при использовании внутри встроенных стрелочных функций.
Синтаксис стрелочной функции короткий, что хорошо. Но в качестве побочного эффекта он может быть непонятным, если имеется сразу много вложенных стрелочных функций.
Давайте рассмотрим следующий сценарий. При нажатии кнопки на сервер отправляется запрос. Когда ответ готов, элементы регистрируются в консоли:
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
Комментарии