Что такое тернарный оператор и как он работает в JavaScript?

👨‍💻 Frontend Developer 💡 Низкий 🎚️ Легкий
#JavaScript #База JS

Краткий ответ

Тернарный оператор — это сокращённая форма записи условного выражения в JavaScript:

условие ? значение_если_true : значение_если_false
  • Синтаксис: condition ? valueIfTrue : valueIfFalse
  • Возвращает: одно из двух значений в зависимости от условия
  • Альтернатива: конструкции if-else для простых случаев
  • Преимущества: краткость, читаемость, возможность использования в выражениях

Синтаксис и основы

ЭлементОписаниеПример
УсловиеВыражение, которое приводится к booleanage >= 18
?Оператор “если”?
Значение trueВозвращается, если условие истинно"Совершеннолетний"
:Разделитель “иначе”:
Значение falseВозвращается, если условие ложно"Несовершеннолетний"

Базовый синтаксис

// Общая форма
const result = condition ? valueIfTrue : valueIfFalse;
 
// Простой пример
const age = 20;
const status = age >= 18 ? "Взрослый" : "Ребёнок";
console.log(status); // "Взрослый"
 
// Эквивалент с if-else
let status2;
if (age >= 18) {
  status2 = "Взрослый";
} else {
  status2 = "Ребёнок";
}

1. Основные примеры использования

Простые условия

// Проверка чётности числа
const number = 42;
const parity = number % 2 === 0 ? "чётное" : "нечётное";
console.log(`Число ${number} ${parity}`); // "Число 42 чётное"
 
// Определение знака числа
const value = -5;
const sign = value >= 0 ? "положительное" : "отрицательное";
console.log(`Число ${sign}`); // "Число отрицательное"
 
// Проверка на пустую строку
const input = "";
const message = input ? "Есть данные" : "Нет данных";
console.log(message); // "Нет данных"

Работа с переменными

// Установка значения по умолчанию
const userName = null;
const displayName = userName ? userName : "Гость";
console.log(`Привет, ${displayName}!`); // "Привет, Гость!"
 
// Выбор между двумя значениями
const theme = "dark";
const backgroundColor = theme === "dark" ? "#333" : "#fff";
const textColor = theme === "dark" ? "#fff" : "#333";
 
// Условное присваивание
const isLoggedIn = true;
const menuItems = isLoggedIn ? ["Профиль", "Настройки", "Выход"] : ["Вход", "Регистрация"];

Математические операции

// Выбор максимального значения
const a = 10, b = 15;
const max = a > b ? a : b;
console.log(`Максимум: ${max}`); // "Максимум: 15"
 
// Абсолютное значение
const num = -7;
const absolute = num >= 0 ? num : -num;
console.log(`|${num}| = ${absolute}`); // "|-7| = 7"
 
// Ограничение значения
const score = 150;
const limitedScore = score > 100 ? 100 : score;
console.log(`Ограниченный счёт: ${limitedScore}`); // "Ограниченный счёт: 100"

2. Вложенные тернарные операторы

Множественные условия

// Оценка по баллам
const score = 85;
const grade = score >= 90 ? "A" : 
              score >= 80 ? "B" : 
              score >= 70 ? "C" : 
              score >= 60 ? "D" : "F";
console.log(`Оценка: ${grade}`); // "Оценка: B"
 
// Определение времени суток
const hour = 14;
const timeOfDay = hour < 6 ? "ночь" :
                  hour < 12 ? "утро" :
                  hour < 18 ? "день" : "вечер";
console.log(`Сейчас ${timeOfDay}`); // "Сейчас день"
 
// Категория возраста
const age = 25;
const category = age < 13 ? "ребёнок" :
                 age < 20 ? "подросток" :
                 age < 60 ? "взрослый" : "пожилой";

Сложные условия

// Проверка доступа с несколькими условиями
const user = { role: "admin", isActive: true, hasPermission: true };
const access = user.role === "admin" ? 
               (user.isActive ? 
                (user.hasPermission ? "Полный доступ" : "Ограниченный доступ") : 
                "Аккаунт заблокирован") : 
               "Нет прав администратора";
 
// Более читаемая версия
const access2 = user.role === "admin" && user.isActive && user.hasPermission ? 
                "Полный доступ" : 
                user.role === "admin" && user.isActive ? 
                "Ограниченный доступ" : 
                user.role === "admin" ? 
                "Аккаунт заблокирован" : 
                "Нет прав администратора";

Альтернативы для сложных случаев

// Вместо сложных вложенных тернарных операторов
// лучше использовать функции или switch
 
function getAccessLevel(user) {
  if (user.role !== "admin") {
    return "Нет прав администратора";
  }
  
  if (!user.isActive) {
    return "Аккаунт заблокирован";
  }
  
  return user.hasPermission ? "Полный доступ" : "Ограниченный доступ";
}
 
// Или с использованием объекта-карты
const gradeMap = {
  90: "A", 80: "B", 70: "C", 60: "D"
};
 
function getGrade(score) {
  const threshold = Object.keys(gradeMap)
    .map(Number)
    .find(threshold => score >= threshold);
  return gradeMap[threshold] || "F";
}

3. Тернарный оператор в функциях

Возврат значений

// Простая функция с тернарным оператором
function isEven(number) {
  return number % 2 === 0 ? true : false;
  // Ещё лучше: return number % 2 === 0;
}
 
// Функция приветствия
function greet(name, timeOfDay) {
  const greeting = timeOfDay === "morning" ? "Доброе утро" :
                   timeOfDay === "afternoon" ? "Добрый день" :
                   timeOfDay === "evening" ? "Добрый вечер" : "Привет";
  return `${greeting}, ${name}!`;
}
 
// Функция валидации
function validateAge(age) {
  return age >= 0 && age <= 120 ? 
         { valid: true, message: "Возраст корректен" } :
         { valid: false, message: "Некорректный возраст" };
}

Условные вызовы функций

// Условный вызов функции
const isDebug = true;
const log = isDebug ? console.log : () => {};
log("Отладочное сообщение"); // Выведется только в режиме отладки
 
// Выбор обработчика
const userType = "premium";
const processPayment = userType === "premium" ? 
                       processPremiumPayment : 
                       processRegularPayment;
 
function processPremiumPayment(amount) {
  return amount * 0.9; // 10% скидка
}
 
function processRegularPayment(amount) {
  return amount;
}
 
// Условное выполнение
const shouldSendEmail = true;
shouldSendEmail ? sendWelcomeEmail() : logSkippedEmail();

4. Тернарный оператор в JSX и шаблонах

React компоненты

// Условный рендеринг в React
function UserProfile({ user, isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <div>
          <h1>Добро пожаловать, {user.name}!</h1>
          <p>Email: {user.email}</p>
        </div>
      ) : (
        <div>
          <h1>Пожалуйста, войдите в систему</h1>
          <button>Войти</button>
        </div>
      )}
    </div>
  );
}
 
// Условные классы CSS
function Button({ isPrimary, isDisabled, children }) {
  const className = `btn ${
    isPrimary ? 'btn-primary' : 'btn-secondary'
  } ${
    isDisabled ? 'btn-disabled' : ''
  }`;
  
  return (
    <button className={className} disabled={isDisabled}>
      {children}
    </button>
  );
}

Шаблонные строки

// Условный текст в шаблонах
const items = ["яблоко", "банан", "апельсин"];
const message = `У вас ${items.length} ${items.length === 1 ? 'товар' : 'товаров'} в корзине`;
 
// Условное форматирование
const price = 1234.56;
const currency = "RUB";
const formattedPrice = `${price.toFixed(2)} ${
  currency === "RUB" ? "₽" : 
  currency === "USD" ? "$" : 
  currency === "EUR" ? "€" : currency
}`;
 
// Условные стили
const status = "error";
const statusMessage = `<span style="color: ${
  status === "success" ? "green" :
  status === "warning" ? "orange" :
  status === "error" ? "red" : "black"
}">${status}</span>`;

5. Сравнение с if-else

КритерийТернарный операторif-else
Краткость✅ Очень краткий❌ Более многословный
Читаемость✅ Для простых условий✅ Для сложных условий
Возврат значения✅ Выражение❌ Требует переменную
Вложенность❌ Может быть сложной✅ Более понятная
Производительность✅ Немного быстрее✅ Практически равная
Отладка❌ Сложнее✅ Проще

Когда использовать тернарный оператор

// ✅ ХОРОШО: Простые условия
const status = isOnline ? "В сети" : "Не в сети";
const color = isDark ? "white" : "black";
const price = hasDiscount ? originalPrice * 0.8 : originalPrice;
 
// ✅ ХОРОШО: Присваивание значений
const greeting = hour < 12 ? "Доброе утро" : "Добрый день";
const icon = isLoading ? "spinner" : "check";
 
// ✅ ХОРОШО: Возврат из функции
function getDiscount(userType) {
  return userType === "premium" ? 0.2 : 0.1;
}
 
// ✅ ХОРОШО: В выражениях
console.log(`Результат: ${isSuccess ? "Успех" : "Ошибка"}`);
array.filter(item => item.active ? item : null);

Когда использовать if-else

// ✅ ХОРОШО: Сложная логика
if (user.role === "admin" && user.permissions.includes("write")) {
  allowEdit = true;
  logAction("edit_allowed", user.id);
  sendNotification("Admin edit access granted");
} else {
  allowEdit = false;
  logAction("edit_denied", user.id);
  showErrorMessage("Insufficient permissions");
}
 
// ✅ ХОРОШО: Множественные действия
if (isFormValid) {
  submitForm();
  showSuccessMessage();
  redirectToNextPage();
} else {
  highlightErrors();
  focusFirstError();
  trackValidationError();
}
 
// ✅ ХОРОШО: Сложные вложенные условия
if (weather.temperature > 25) {
  if (weather.humidity > 70) {
    recommendation = "Жарко и влажно, оставайтесь дома";
  } else {
    recommendation = "Отличная погода для прогулки";
  }
} else if (weather.temperature < 0) {
  recommendation = "Холодно, одевайтесь теплее";
} else {
  recommendation = "Приятная погода";
}

Практические задачи

Задача 1: Что выведет консоль?

const a = 5;
const b = 10;
const result = a > b ? a * 2 : b / 2;
console.log(result);
 
const x = 0;
const y = x ? "true" : "false";
console.log(y);
 
const str = "";
const output = str ? str.toUpperCase() : "empty";
console.log(output);
Ответ

5 (поскольку 5 не больше 10, возвращается b / 2 = 10 / 2 = 5) "false" (0 приводится к false) "empty" (пустая строка приводится к false)

Задача 2: Упростите код

let message;
if (user.isVip) {
  message = "Добро пожаловать, VIP пользователь!";
} else {
  message = "Добро пожаловать!";
}
 
let discount;
if (order.total > 1000) {
  discount = 0.1;
} else {
  discount = 0;
}
Ответ
const message = user.isVip ? 
  "Добро пожаловать, VIP пользователь!" : 
  "Добро пожаловать!";
 
const discount = order.total > 1000 ? 0.1 : 0;

Задача 3: Создайте функцию определения сезона

// Создайте функцию getSeason, которая принимает номер месяца (1-12)
// и возвращает название сезона
function getSeason(month) {
  // Ваш код здесь
}
 
console.log(getSeason(3)); // "весна"
console.log(getSeason(7)); // "лето"
console.log(getSeason(10)); // "осень"
console.log(getSeason(1)); // "зима"
Ответ
function getSeason(month) {
  return month >= 3 && month <= 5 ? "весна" :
         month >= 6 && month <= 8 ? "лето" :
         month >= 9 && month <= 11 ? "осень" : "зима";
}
 
// Альтернативный вариант
function getSeason(month) {
  return month === 12 || month <= 2 ? "зима" :
         month <= 5 ? "весна" :
         month <= 8 ? "лето" : "осень";
}

Задача 4: Валидация формы

// Создайте функцию validateInput, которая проверяет:
// - email содержит @
// - password длиннее 6 символов
// - age больше 0 и меньше 120
// Возвращает объект с результатом валидации
 
function validateInput(email, password, age) {
  // Ваш код здесь
}
 
const result = validateInput("test@mail.com", "123456789", 25);
console.log(result); // { valid: true, errors: [] }
Ответ
function validateInput(email, password, age) {
  const errors = [];
  
  !email.includes("@") ? errors.push("Некорректный email") : null;
  password.length <= 6 ? errors.push("Пароль слишком короткий") : null;
  age <= 0 || age >= 120 ? errors.push("Некорректный возраст") : null;
  
  return {
    valid: errors.length === 0,
    errors: errors
  };
}
 
// Более читаемая версия
function validateInput(email, password, age) {
  const isEmailValid = email.includes("@");
  const isPasswordValid = password.length > 6;
  const isAgeValid = age > 0 && age < 120;
  
  const errors = [
    ...(!isEmailValid ? ["Некорректный email"] : []),
    ...(!isPasswordValid ? ["Пароль слишком короткий"] : []),
    ...(!isAgeValid ? ["Некорректный возраст"] : [])
  ];
  
  return {
    valid: errors.length === 0,
    errors
  };
}

Задача 5: Оптимизация производительности

// Какой вариант быстрее и почему?
 
// Вариант A
function getStatusA(user) {
  if (user.isActive) {
    return "Активен";
  } else {
    return "Неактивен";
  }
}
 
// Вариант B
function getStatusB(user) {
  return user.isActive ? "Активен" : "Неактивен";
}
 
// Вариант C
const getStatusC = (user) => user.isActive ? "Активен" : "Неактивен";
Ответ

Все варианты имеют практически одинаковую производительность. Современные JavaScript движки оптимизируют код на уровне байт-кода.

Вариант B и C предпочтительнее по следующим причинам:

  • Меньше кода
  • Более функциональный стиль
  • Легче читать для простых условий
  • Меньше места для ошибок

Вариант A лучше для сложной логики с множественными действиями.


Продвинутые техники

Тернарный оператор с деструктуризацией

// Условная деструктуризация
const user = { name: "Иван", email: "ivan@mail.com" };
const { name, email = "Не указан" } = user.email ? user : {};
 
// Условное присваивание с деструктуризацией
const config = isDevelopment ? 
  { apiUrl: "http://localhost:3000", debug: true } :
  { apiUrl: "https://api.production.com", debug: false };
 
const { apiUrl, debug } = config;
 
// Условный spread
const baseStyles = { padding: "10px", margin: "5px" };
const buttonStyles = {
  ...baseStyles,
  ...(isPrimary ? { backgroundColor: "blue", color: "white" } : {}),
  ...(isLarge ? { fontSize: "18px", padding: "15px" } : {})
};

Функциональное программирование

// Тернарный оператор в методах массивов
const numbers = [1, 2, 3, 4, 5];
 
// Условная фильтрация
const filtered = numbers.filter(n => showEven ? n % 2 === 0 : n % 2 !== 0);
 
// Условное преобразование
const transformed = numbers.map(n => 
  n > 3 ? n * 2 : n
);
 
// Условная сортировка
const sorted = numbers.sort((a, b) => 
  ascending ? a - b : b - a
);
 
// Условное сведение
const result = numbers.reduce((acc, n) => 
  n % 2 === 0 ? acc + n : acc, 0
);

Каррирование и частичное применение

// Функция высшего порядка с тернарным оператором
const createValidator = (condition) => (value) => 
  condition(value) ? { valid: true } : { valid: false, error: "Validation failed" };
 
const isPositive = (n) => n > 0;
const validatePositive = createValidator(isPositive);
 
console.log(validatePositive(5)); // { valid: true }
console.log(validatePositive(-1)); // { valid: false, error: "Validation failed" }
 
// Условное каррирование
const mathOperation = (operation) => (a) => (b) => 
  operation === "add" ? a + b :
  operation === "subtract" ? a - b :
  operation === "multiply" ? a * b :
  operation === "divide" ? (b !== 0 ? a / b : "Division by zero") :
  "Unknown operation";
 
const add = mathOperation("add");
const multiply = mathOperation("multiply");
 
console.log(add(5)(3)); // 8
console.log(multiply(4)(7)); // 28

Мемоизация с условиями

// Условная мемоизация
function createMemoizedFunction(fn, shouldMemoize = true) {
  const cache = new Map();
  
  return function(...args) {
    const key = JSON.stringify(args);
    
    return shouldMemoize && cache.has(key) ? 
      cache.get(key) :
      (() => {
        const result = fn.apply(this, args);
        shouldMemoize ? cache.set(key, result) : null;
        return result;
      })();
  };
}
 
// Условное кеширование API запросов
const apiCall = createMemoizedFunction(
  async (url) => {
    const response = await fetch(url);
    return response.json();
  },
  process.env.NODE_ENV === "production" // Кешируем только в продакшене
);

Частые ошибки и подводные камни

1. Сложные вложенные операторы

// ❌ ПЛОХО: Слишком сложно для понимания
const result = a > b ? 
               (c > d ? 
                (e > f ? "case1" : "case2") : 
                (g > h ? "case3" : "case4")) : 
               (i > j ? 
                (k > l ? "case5" : "case6") : 
                "case7");
 
// ✅ ХОРОШО: Используйте функции или switch
function getComplexResult(a, b, c, d, e, f, g, h, i, j, k, l) {
  if (a <= b) {
    return i > j ? (k > l ? "case5" : "case6") : "case7";
  }
  
  if (c > d) {
    return e > f ? "case1" : "case2";
  }
  
  return g > h ? "case3" : "case4";
}

2. Побочные эффекты в условиях

// ❌ ПЛОХО: Побочные эффекты в условии
let counter = 0;
const result = ++counter > 5 ? "high" : "low";
 
// ❌ ПЛОХО: Мутация в тернарном операторе
const processArray = (arr) => 
  arr.length > 0 ? arr.push("new item") : arr;
 
// ✅ ХОРОШО: Чистые функции
let counter = 0;
counter++;
const result = counter > 5 ? "high" : "low";
 
// ✅ ХОРОШО: Без мутаций
const processArray = (arr) => 
  arr.length > 0 ? [...arr, "new item"] : arr;

3. Неправильная приоритетность операторов

// ❌ ПЛОХО: Неясная приоритетность
const result = a + b > c ? d * e : f / g + h;
 
// ✅ ХОРОШО: Явные скобки
const result = (a + b) > c ? (d * e) : ((f / g) + h);
 
// ❌ ПЛОХО: Смешивание с логическими операторами
const value = isTrue && condition ? "yes" : "no" || "maybe";
 
// ✅ ХОРОШО: Явная группировка
const value = (isTrue && condition) ? "yes" : ("no" || "maybe");

4. Проблемы с типами

// ❌ ПЛОХО: Неожиданное приведение типов
const result = someValue ? "string" : 0; // Может вернуть разные типы
 
// ✅ ХОРОШО: Согласованные типы
const result = someValue ? "has value" : "no value";
const numericResult = someValue ? 1 : 0;
 
// ❌ ПЛОХО: Проблемы с null/undefined
const name = user.name ? user.name : "Unknown"; // Не сработает для пустой строки
 
// ✅ ХОРОШО: Правильная проверка
const name = user.name != null && user.name !== "" ? user.name : "Unknown";
// Или с nullish coalescing (ES2020)
const name = user.name ?? "Unknown";

Современные альтернативы

Nullish Coalescing Operator (??)

// Старый способ с тернарным оператором
const value1 = someValue !== null && someValue !== undefined ? someValue : "default";
 
// Новый способ с nullish coalescing
const value2 = someValue ?? "default";
 
// Разница в поведении
const test1 = "" ? "not empty" : "empty"; // "empty"
const test2 = "" ?? "empty"; // "" (пустая строка не null/undefined)
 
const test3 = 0 ? "not zero" : "zero"; // "zero"
const test4 = 0 ?? "zero"; // 0 (ноль не null/undefined)

Optional Chaining (?.)

// Старый способ с тернарным оператором
const street = user && user.address && user.address.street ? 
               user.address.street : 
               "Адрес не указан";
 
// Новый способ с optional chaining
const street = user?.address?.street ?? "Адрес не указан";
 
// Условный вызов методов
const result = user?.getName ? user.getName() : "Имя не доступно";
// Или
const result = user?.getName?.() ?? "Имя не доступно";

Логические операторы присваивания

// ES2021: Логическое присваивание
let config = {};
 
// Старый способ
config.theme = config.theme ? config.theme : "light";
 
// С тернарным оператором
config.theme = config.theme ? config.theme : "light";
 
// Новый способ
config.theme ||= "light";
config.debug ??= false;
config.features &&= config.features.filter(f => f.enabled);

Лучшие практики

1. Читаемость превыше всего

// ✅ ХОРОШО: Простые условия
const status = isOnline ? "online" : "offline";
const price = hasDiscount ? originalPrice * 0.8 : originalPrice;
 
// ✅ ХОРОШО: Разбивка на строки для сложных случаев
const message = user.isVip ? 
  `Добро пожаловать, ${user.name}! У вас VIP статус.` :
  `Добро пожаловать, ${user.name}!`;
 
// ✅ ХОРОШО: Вынос условий в переменные
const isEligibleForDiscount = user.isVip || user.purchaseCount > 10;
const finalPrice = isEligibleForDiscount ? price * 0.9 : price;

2. Согласованность типов

// ✅ ХОРОШО: Одинаковые типы
const result = isSuccess ? "Успех" : "Ошибка";
const count = hasItems ? items.length : 0;
const handler = isEnabled ? enabledHandler : disabledHandler;
 
// ❌ ИЗБЕГАЙТЕ: Разные типы
const mixed = isSuccess ? "Success" : false; // string | boolean
const confusing = hasData ? data : null; // object | null

3. Производительность

// ✅ ХОРОШО: Дешёвые операции в условии
const result = isSimpleFlag ? expensiveOperation() : defaultValue;
 
// ❌ ИЗБЕГАЙТЕ: Дорогие операции в условии
const bad = expensiveCheck() ? value1 : value2;
 
// ✅ ЛУЧШЕ: Кеширование результата
const isExpensive = expensiveCheck();
const good = isExpensive ? value1 : value2;

4. Тестируемость

// ✅ ХОРОШО: Легко тестировать
function getDiscountedPrice(price, userType) {
  return userType === "premium" ? price * 0.8 : price;
}
 
// ✅ ХОРОШО: Чистые функции
const formatCurrency = (amount, currency) => 
  currency === "USD" ? `$${amount}` : `${amount} ${currency}`;
 
// ❌ ИЗБЕГАЙТЕ: Зависимость от внешнего состояния
const formatPrice = (amount) => 
  globalCurrency === "USD" ? `$${amount}` : `${amount} ${globalCurrency}`;

Резюме

Тернарный оператор — это мощный инструмент для написания краткого и выразительного кода. Важно помнить:

Используйте для простых условий — одно условие, два возможных значения
Поддерживайте читаемость — если код сложно понять, используйте if-else
Следите за типами — возвращаемые значения должны быть согласованы
Избегайте побочных эффектов — тернарный оператор для выражений, не действий
Комбинируйте с современными операторами — ??, ?., ||=, &&=, ??=

В современной разработке предпочитайте:

  • Тернарный оператор для простых условий
  • if-else для сложной логики
  • Nullish coalescing (??) для значений по умолчанию
  • Optional chaining (?.) для безопасного доступа к свойствам
  • Функции для повторяющейся условной логики

Освойте тернарный оператор, чтобы писать более элегантный и функциональный JavaScript код! 🚀