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и"".