Как сделать JS-код короче и понятнее
Рассмотрим, как сделать код на JavaScript короче и понятнее, умело расставляя return в нужных местах.
else после return (не нужен)
Первый трюк — вынесение одной из ветвей if / else на уровень выше. Для примера возьмём функцию сравнения массивов:
function iaArrayEqual(arr1, arr2) { if (arr1.length !== arr2.length) { return false; } else { return arr1.every((el, i) => el === arr2[i]); } }
Если при выполнении мы попали в первый if, то дальше и так уже не пойдём, потому что вышли из функции. Значит, else можно просто убрать:
function iaArrayEqual(arr) { if (array.length === 0) { return false; } return arr1.every((el, i) => el === arr2[i]); }
Мы сделали код не только короче, но и понятнее: сначала обрабатываем специальный случай, потом, если не получилось, — переходим к основной логике. Даже просто думать про алгоритмы в таких терминах уже полезно. Приятный бонус: теперь более частый и важный код стоит левее, так что его проще заметить и прочитать.
Ещё круче этот метод работает, когда особых случаев несколько. Посмотрите сами на примере несложного реакт-компонента:
const UserList = ({ users, isError }) => { if (users.length === 0) { return <Empty />; } else { if (!isError) { return <Users users={users} />; } else { return <Error />; } } }; const UserList2 = ({ users, isError }) => { if (users.length === 0) { return <Empty />; } if (isError) { return <Error /> } return <Users users={this.users} />; };
Функционалный инициализатор
Второй трюк — вынести сложную логику инициализации в функцию. Тернарный оператор помогает инициализирвать переменную одним из двух значений по условию: const userName = user ? user.name : 'unknown';. Но как только добавляется ветвь, начинаются проблемы — в лучшем случае это выглядит так:
let userName = 'unknown'; if (user) { userName = user.name } else if (config.defaultUser) { userName = config.defaultUser.name }
Из плохого: мы отказались от const ради единственного присваивания, но потеряли его защиту во всём блоке. Кроме того, теперь при чтении кода мы в первую очередь увидим самое запасное значение переменной. Для таких случаев мне нравится заводить функцию-инициализатор (хотите показаться чуть умнее — используйте слово фабрика):
const getUserName = (user, defaultUser) => { if (user) return user.name; if (defaultUser) return defaultUser.name; return 'unknown'; }; const userName = getUserName(user, defaultUser);
Обратите внимание, что я использую первый трюк, но "неправильно" — специальные случаи обрабатываются внизу. В данном случае это логичнее — мы падаем вниз, пока одна из страховок не сработает.
return из switch
Наконец, ещё одно применение похожего трюка. У конструкции switch симпатичная задумка, но странноватый синтаксис — в ветках нет блоков, и код выполняется от первого подходящего case до ближайшего break. В коде самого частого кейса получается больше синтаксиса, чем логики:
let msg = 'Something happened'; switch (event) { case 'err': msg = 'An error occurred'; break; case 'fire': msg = 'There\'s a fire'; break; case 'ready': msg = 'All set and ready to roll'; break; } return msg;
Если вы внимательно следили за первыми двумя трюками, то легко примените их к этому случаю:
function makeMsg(event) { switch (event) { case 'err': return 'An error occurred'; case 'fire': return 'There\'s a fire'; case 'ready': return 'All set and ready to roll'; } return 'Unknown'; } const msg = makeMsg(event);
Кстати, именно так обычно записывают redux-редьюсеры. Если зайти ещё дальше, весь switch вообще можно превратить из кода в конфигурацию:
const MESSAGES = { 'err': 'An error occurred', 'fire': 'There\'s a fire', 'ready': 'All set and ready to roll', }; const DEFAULT_MESSAGE = 'Unknown'; const makeMsg = event => MESSAGES[event] || DEFAULT_MSG;
Впрочем, мне такой вариант не очень нравится — если в одном из случаев нужно добавить специальную логику, у нас проблемы.
Это мои любимые трюки в области код-стайла — когда я вижу огромный легаси-код с сотней вложенных ветвлений, за которыми стало невозможно уследить ещё лет 10 назад, я начинаю очень аккуратно разбирать его на блоки, выносить специальные случаи и извлекать основную логику — и скоро в нём могут разобраться не только стажеры, но даже и я сам.