$ sudo teach IT
РАЗДЕЛ 3 · УРОК 3

Логические операторы: &&, ||, !, ??

Иногда одного условия мало. «Пользователь авторизован и оплатил подписку» — это два условия одновременно. «Есть скидка или пришёл промокод» — хотя бы одно из двух. Логические операторы позволяют комбинировать условия в любые сочетания.

⏱ ~25 минут 🎓 Для новичков ⚡ JavaScript

🤝 && — логическое «И» (AND)

Оператор && читается как «И». Он возвращает true только тогда, когда оба операнда истинны. Если хотя бы один из них false — результат false.

Жизненный пример: «Чтобы попасть в клуб, нужно быть совершеннолетним И иметь членский билет». Оба условия должны быть выполнены одновременно.

A B A && B
truetruetrue ✓
truefalsefalse ✗
falsetruefalse ✗
falsefalsefalse ✗
let isAdult = true;
let hasMembership = true;

if (isAdult && hasMembership) {
  console.log("Добро пожаловать в клуб!");
} else {
  console.log("Вход запрещён");
}
// Вывод: "Добро пожаловать в клуб!"

// Если одно условие false
let isLoggedIn = true;
let isPremium = false;

if (isLoggedIn && isPremium) {
  console.log("Доступ к премиум-контенту");
} else {
  console.log("Оформите подписку");
}
// Вывод: "Оформите подписку"

Можно комбинировать с операторами сравнения:

let age = 25;
let salary = 80000;

// Подходит ли кандидат на ипотеку?
if (age >= 21 && age <= 65 && salary >= 50000) {
  console.log("Заявка одобрена");
} else {
  console.log("Заявка отклонена");
}
// Вывод: "Заявка одобрена"

🔀 || — логическое «ИЛИ» (OR)

Оператор || читается как «ИЛИ». Он возвращает true, если хотя бы один из операндов истинен. Только при обоих false результат false.

Жизненный пример: «Доставка бесплатная, если заказ на сумму больше 2000₽ или у тебя есть промокод». Достаточно одного из двух.

A B A || B
truetruetrue ✓
truefalsetrue ✓
falsetruetrue ✓
falsefalsefalse ✗
let orderTotal = 1500;
let hasPromoCode = true;

if (orderTotal >= 2000 || hasPromoCode) {
  console.log("Доставка бесплатная!");
} else {
  console.log("Доставка: 300₽");
}
// Вывод: "Доставка бесплатная!" — hasPromoCode = true

// Проверка роли пользователя
let role = "moderator";
if (role === "admin" || role === "moderator") {
  console.log("Доступ к панели управления");
}

🔄 ! — логическое «НЕ» (NOT)

Оператор ! — это отрицание. Он просто переворачивает значение: true становится false, а false становится true.

let isLoggedIn = false;

// Проверяем: пользователь НЕ авторизован?
if (!isLoggedIn) {
  console.log("Пожалуйста, войдите в аккаунт");
}
// Вывод: "Пожалуйста, войдите в аккаунт"

console.log(!true);   // false
console.log(!false);  // true
console.log(!0);      // true  (0 falsy, !falsy = true)
console.log(!"");     // true  ("" falsy, !falsy = true)
console.log(!"hello");// false ("hello" truthy, !truthy = false)

// Двойное отрицание !! — преобразует в булево значение
console.log(!!0);     // false
console.log(!!1);     // true
console.log(!!"");    // false
console.log(!!"hello"); // true

💡 Трюк: двойное отрицание !! — удобный способ превратить любое значение в явное true или false. !!"hello" → true, !!0 → false.

⚡ Ленивые вычисления (Short-circuit evaluation)

Это одна из самых важных особенностей логических операторов в JavaScript. Понимание этого механизма — признак опытного разработчика.

Суть: JavaScript не вычисляет правую часть логического выражения, если результат уже известен из левой части.

🔗 Short-circuit у &&

Если левая часть у && — false, правая часть не вычисляется вообще. Ведь если одно из «И» уже false — результат точно false, зачем считать дальше?

let user = null;

// БЕЗОПАСНО — второй операнд не вычислится если user null
if (user && user.name === "Admin") {
  console.log("Привет, Админ!");
}
// Нет ошибки! user — null (falsy), правая часть пропускается.
// Без && мы бы получили: Cannot read property 'name' of null

🔀 Short-circuit у ||

Если левая часть у || — true, правая часть не вычисляется. Результат уже true, зачем смотреть дальше?

// || часто используется для значений по умолчанию
let userName = "";  // пустая строка — пользователь не ввёл имя

let displayName = userName || "Гость";
console.log(displayName);  // "Гость"
// userName — falsy, поэтому || берёт правую часть

let realName = "Иван";
let display2 = realName || "Гость";
console.log(display2);  // "Иван"
// realName — truthy, || возвращает левую часть

🧠 Важно понять: && и || возвращают не просто true/false, а одно из своих значений. A && B вернёт A, если A — falsy (и остановится), или B, если A — truthy. A || B вернёт A, если A — truthy (и остановится), или B, если A — falsy.

// && возвращает первое falsy или последнее значение
console.log(1 && 2 && 3);   // 3   (всё truthy, вернул последнее)
console.log(1 && 0 && 3);   // 0   (нашёл falsy — вернул его)
console.log(false && "что угодно");  // false

// || возвращает первое truthy или последнее значение
console.log(0 || "" || "fallback"); // "fallback" (первое truthy)
console.log(0 || 5 || 10);         // 5 (нашёл truthy — вернул его)
console.log(false || null);        // null (последнее — оба falsy)

❓ ?? — оператор нулевого слияния (Nullish Coalescing)

Оператор ?? появился в ES2020 и решает конкретную проблему с ||. Посмотрим на неё.

⚠️ Проблема с ||: Допустим, пользователь устанавливает количество товаров в корзине. Он вводит 0 — это значит «ноль товаров», полностью корректное значение. Но 0 || 1 вернёт 1, потому что 0 — falsy! Вы перезапишете правильное значение.

let userCount = 0;  // пользователь хочет 0 товаров
let count = userCount || 1;  // БАГ! count будет 1, а не 0
// || видит 0 как falsy и берёт правую часть

?? решает это: он проверяет не на falsy, а только на null или undefined. Ноль, пустая строка, false — он их пропускает и считает допустимыми значениями.

let userCount = 0;
let count = userCount ?? 1;  // ПРАВИЛЬНО! count = 0
// ?? видит 0 и думает: "это не null и не undefined, оставляем"

let name = null;
let displayName = name ?? "Гость";  // "Гость"
// null → берём правую часть

let status = undefined;
let displayStatus = status ?? "Неизвестно";  // "Неизвестно"
// undefined → берём правую часть

let score = 0;
let displayScore = score ?? "Нет данных";  // 0
// 0 — не null и не undefined → оставляем 0
|| — ИЛИ (для falsy)

Возвращает правую часть, если левая — любое falsy: 0, "", false, null, undefined.

?? — нулевое слияние (для null/undefined)

Возвращает правую часть, только если левая — null или undefined. Ноль и пустая строка — допустимы.

🔢 Приоритет: ! → && → ||

Логические операторы имеют порядок приоритета — как в математике умножение перед сложением. ! имеет самый высокий приоритет, потом &&, потом ||.

// Без скобок — применяется приоритет
let result = true || false && false;
// && выполняется первым: false && false = false
// затем: true || false = true
console.log(result);  // true

// Со скобками — можно изменить порядок
let result2 = (true || false) && false;
// сначала скобки: true || false = true
// затем: true && false = false
console.log(result2);  // false

💡 Совет: если в условии смешаны && и || — добавляйте скобки для ясности. Код должен быть понятен людям, а не только компилятору.

✅ Итоги урока

  • && (AND) — true только если оба операнда true
  • || (OR) — true если хотя бы один операнд true
  • ! (NOT) — инвертирует значение: true→false, false→true
  • Short-circuit: && останавливается на первом false, || — на первом true
  • || используют для значений по умолчанию (но осторожно с 0 и "")
  • ?? — «нулевое слияние», правая часть берётся только если левая null или undefined
  • Приоритет: ! → && → ||; при смешивании — используйте скобки

В следующем уроке изучим тернарный оператор — компактную замену простым if/else, которую вы будете встречать в каждом React-компоненте. 🚀

Тест: Логические операторы

7 вопросов

Проверка доступа через &&

Создайте переменные: age = 21, hasTicket = true. Создайте переменную canEnter, которая true только если age >= 18 И hasTicket === true.

Проверка роли через ||

Создайте переменную role = "moderator". Создайте переменную hasAccess, которая true если role равна "admin" ИЛИ role равна "moderator".