Курсы

Курсы в разработке Подготовительные курсы
Работа в компаниях Компаниям Блог +7 499 110-61-65

Как сделать JS-код короче и понятнее

JS_Deep_20.6_Site-5020-09280b.png

Рассмотрим, как сделать код на 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 назад, я начинаю очень аккуратно разбирать его на блоки, выносить специальные случаи и извлекать основную логику — и скоро в нём могут разобраться не только стажеры, но даже и я сам.

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться