Learn JavaScript

Guard clauses: ранний выход из функции

Guard clauses - это стиль написания условий, где функция выходит как можно раньше, если дальше выполнять код бессмысленно. Обычно это делают через ранний return или throw. Такой подход уменьшает вложенность и делает основной сценарий более заметным.

Идея раннего выхода

Есть два типа веток в логике функции:

  • основной сценарий - то, что должно произойти “в норме”
  • проверки и исключения - ситуации, где нужно остановиться (нет данных, неверный формат, нет доступа)

Guard clauses выносят проверки наверх, а основной сценарий оставляют ниже - без лишних else.

Пример: меньше вложенности

Без guard clauses код часто превращается в “лес” из вложенных условий:

function formatName(user) {
  if (user) {
    if (user.name) {
      return user.name.trim();
    } else {
      return "Unknown";
    }
  } else {
    return "Unknown";
  }
}

С guard clauses логика становится прямее:

function formatName(user) {
  if (!user) return "Unknown";
  if (!user.name) return "Unknown";

  return user.name.trim();
}

Основная часть (“вернуть имя”) теперь находится внизу и читается легче.

Guard clauses и проверка входных данных

Часто guard clauses используют для проверки аргументов функции.

function toNumber(value) {
  const n = Number(value);
  if (Number.isNaN(n)) return null;
  return n;
}

console.log(toNumber("10")); // 10
console.log(toNumber("x"));  // null

Здесь “плохой” сценарий обрабатывается сразу, а дальше остается простой путь.

Ранний return в несколько шагов

Guard clauses хорошо работают, когда проверок несколько.

function canAccess(user) {
  if (!user) return false;
  if (!user.isActive) return false;
  if (user.role !== "admin") return false;

  return true;
}

Такой код проще изменять: добавить новую проверку можно одной строкой сверху.

Guard clauses вместо else

else не всегда плох, но часто он не нужен. Если при выполнении условия идет return, то else можно убрать - дальше код и так не выполнится.

function getLabel(isOk) {
  if (isOk) return "ok";
  return "fail";
}

Guard clauses и ошибки: return vs throw

Иногда функция “мягко” сообщает, что не может работать (например, возвращает null). Иногда это действительно ошибка, и лучше выбросить исключение.

Мягкий вариант: вернуть null

function parseId(value) {
  const n = Number(value);
  if (!Number.isInteger(n) || n <= 0) return null;
  return n;
}

Жесткий вариант: throw

function parseIdStrict(value) {
  const n = Number(value);
  if (!Number.isInteger(n) || n <= 0) {
    throw new Error("Invalid id");
  }
  return n;
}

Для учебных примеров обычно достаточно return null, а throw используется, когда неверные данные считаются ошибкой программы.

Где guard clauses особенно полезны

  • проверка аргументов (пустые значения, неверные типы)
  • быстрый выход из функций-обработчиков (например, “ничего не делать, если условие не выполнено”)
  • обработка редких веток и ошибок, чтобы основной сценарий остался “ровным”

Пример с “ничего не делать”:

function save(value) {
  if (value === null || value === undefined) return;

  console.log("saved:", value);
}

save(undefined); // ничего
save("data");    // "saved: data"

Частые ошибки

Слишком много проверок, которые можно объединить

Иногда несколько guard clauses можно сделать компактнее, но важно не терять читаемость.

function canEdit(user) {
  if (!user) return false;
  if (!user.isActive) return false;
  if (user.role !== "editor") return false;
  return true;
}

Такой код может быть лучше, чем одна длинная строка с &&, если проверок много.

Guard clause по truthy/falsy там, где нужна точная проверка

Если значение может быть 0 или "", проверка if (!value) может быть неверной.

function showPage(page) {
  if (!page) return 1; // page = 0 тоже сюда попадет
  return page;
}

Более точный вариант:

function showPage(page) {
  if (page === undefined) return 1;
  return page;
}

Что стоит запомнить

  • Guard clauses - это ранние выходы, которые убирают лишнюю вложенность.
  • Проверки выносятся наверх, основной сценарий остается ровным и читаемым.
  • else часто можно не писать, если внутри if есть return.
  • Важно выбирать точные проверки, чтобы не сломать логику на 0 и "".