Как сделать JS-код короче и понятнее
Рассмотрим, как сделать код на JavaScript короче и понятнее, умело расставляя
else после return (не нужен)
Первый трюк — вынесение одной из ветвей
function iaArrayEqual(arr1, arr2) { if (arr1.length !== arr2.length) { return false; } else { return arr1.every((el, i) => el === arr2[i]); } }
Если при выполнении мы попали в первый
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} />; };
Функционалный инициализатор
Второй трюк — вынести сложную логику инициализации в функцию. Тернарный оператор помогает инициализирвать переменную одним из двух значений по условию:
let userName = 'unknown'; if (user) { userName = user.name } else if (config.defaultUser) { userName = config.defaultUser.name }
Из плохого: мы отказались от
const getUserName = (user, defaultUser) => { if (user) return user.name; if (defaultUser) return defaultUser.name; return 'unknown'; }; const userName = getUserName(user, defaultUser);
Обратите внимание, что я использую первый трюк, но "неправильно" — специальные случаи обрабатываются внизу. В данном случае это логичнее — мы падаем вниз, пока одна из страховок не сработает.
return из switch
Наконец, ещё одно применение похожего трюка. У конструкции
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-редьюсеры. Если зайти ещё дальше, весь
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 назад, я начинаю очень аккуратно разбирать его на блоки, выносить специальные случаи и извлекать основную логику — и скоро в нём могут разобраться не только стажеры, но даже и я сам.