Тернарный оператор и приоритеты

Часто по условию нужно не выполнить разные действия, а просто выбрать одно из двух значений: текст кнопки, цену со скидкой, статус. Через if..else такой выбор выглядит громоздко:

status-if.jsjs
const age = 20;let status;if (age >= 18) {  status = 'взрослый';} else {  status = 'ребёнок';}console.log(status);
Консоль
взрослый

Обе ветки делают одно и то же – выбирают значение для status. При записи через if..else переменную приходится объявить заранее через let, потому что значение присваивается позже, внутри одной из веток.

Для такого выбора в JavaScript есть тернарный оператор – единственный оператор с тремя операндами: условие ? значениеЕслиИстина : значениеЕслиЛожь. Он сам возвращает выбранное значение, поэтому результат можно сразу записать в const:

status-ternary.jsjs
const age = 20;const status = age >= 18 ? 'взрослый' : 'ребёнок';console.log(status);
Консоль
взрослый

Читается вслух: возраст не меньше 18? тогда «взрослый», иначе «ребёнок». Разберём запись:

age >= 18 ? 'взрослый' : 'ребёнок' условие ответ, если да ответ, если нет читается как вопрос граница между двумя ответами

Запомнить знаки просто: вопросительный знак читается как вопрос, двоеточие – граница между двумя ответами. Сначала вычисляется условие; из двух ответов вычисляется только выбранный – до второго дело не доходит. По шагам:

Тернарник: вопрос, выбор, значение
const age = 20;
В age – число 20.
const status = age >= 18 ? 'взрослый' : 'ребёнок';
console.log(status);
Консоль
Нет вывода в консоль.

Выбор значения, а не сценарий

Главное разграничение: тернарный оператор выбирает одно из двух значений, а if..else управляет разными сценариями действий. Если обе ветки только присваивают одной переменной разные значения, чаще всего лучше использовать тернарный оператор:

choices.jsjs
const isSaved = true;const total = 4200;const n = 7;// текст кнопкиconst label = isSaved ? 'Сохранено' : 'Сохранить';// цена доставкиconst delivery = total >= 3000 ? 0 : 300;// подпись числаconst parity = n % 2 === 0 ? 'чётное' : 'нечётное';

Другое дело, когда ветки выполняют разные действия. При большом заказе программа печатает две строки, при обычном – одну. Это уже не выбор значения, а выбор сценария, поэтому здесь уместнее if..else:

scenarios.jsjs
const total = 6000;if (total >= 5000) {  console.log('бесплатная доставка');  console.log('подарок в заказе');} else {  console.log('доставка 300');}
Консоль
бесплатная доставка
подарок в заказе

Тернарник – выражение

Ещё одно отличие. Тернарник – выражение: после вычисления на его месте остаётся одно значение – так же, как 2 + 2 превращается в 4. Поэтому его можно писать всюду, где ждут значение: справа от =, внутри console.log(...), в шаблонной строке. if..else в значение не превращается – он только выполняет одну из веток, поэтому справа от = его не поставить.

expression.jsjs
const items = 3;// выражение можно присвоить:const note = items > 0 ? 'корзина не пуста' : 'корзина пуста';// и передать аргументом:console.log(items > 0 ? 'корзина не пуста' : 'корзина пуста');// if – инструкция, у неё значения нет; так написать нельзя:// const note = if (items > 0) { ... } else { ... }
Консоль
корзина не пуста

Особенно это удобно в шаблонной строке: тернарник пишут прямо внутри ${...}, и подпись собирается на месте:

template-choice.jsjs
const score = 85;console.log(`Результат: ${score >= 60 ? 'зачёт' : 'пересдача'}, баллов: ${score}`);
Консоль
Результат: зачёт, баллов: 85

Когда тернарник мешает

Тернарник хорош ровно на один вопрос. Появился второй – возникает вложенность, и строку приходится распутывать. Та же логика цепочкой else if длиннее, зато каждая ветка видна отдельно:

Второй вопрос
Цепочка: каждая ветка видна
let grade;if (score >= 90) {  grade = 'отлично';} else if (score >= 60) {  grade = 'зачёт';} else {  grade = 'пересдача';}
Два вопроса в одной строке
const grade = score >= 90 ? 'отлично' : score >= 60 ? 'зачёт' : 'пересдача';

Второй сигнал – длинные выражения в ответах: вопрос всего один, но оба ответа – формулы, и выбор между ? и : приходится выискивать. Ответы выносят в переменные с именами – и выбор снова читается одной короткой строкой:

Длинные ответы
Ответы в именованных переменных
const vipPrice = total - total / 10 - 200;const regularPrice = total + total / 5 + 300;const price = isVip ? vipPrice : regularPrice;
Формулы прямо в ответах
const price = isVip ? total - total / 10 - 200 : total + total / 5 + 300;

И третий сигнал – по условию нужно не выбрать значение, а выполнить действия. Записать такое тернарником можно, но его результат никому не нужен – действия записывают через if..else:

Действия вместо значений
if..else: выбор сценария
if (isLate) {  console.log('Вы опоздали');} else {  console.log('Вы вовремя');}
Тернарник ради печати: значение выброшено
isLate ? console.log('Вы опоздали') : console.log('Вы вовремя');

Как JavaScript группирует выражение

У каждого оператора есть приоритет: 2 + 2 * 2 – это 6, умножение сильнее. Ступени арифметики, сравнений и связок собраны в уроке «Логические операторы: ||, && и !» – тернарник и присваивание встают почти в самом хвосте той же лестницы.

В полной таблице на MDN – почти два десятка уровней, и заучивать её не нужно. Рабочее правило: очевидное – без скобок, всё остальное – со скобками. Скобки бесплатны, а читают код чаще, чем пишут.

Чем ниже ступень, тем ниже приоритет.

Как лестница работает на практике – одно выражение, пять ступеней:

Пять ступеней одной строки
const age = 20;
В age – 20, в hasTickettrue.
const hasTicket = true;
const answer = age >= 6 + 12 && hasTicket ? 'пускаем' : 'нельзя';
console.log(answer);
Консоль
Нет вывода в консоль.
Переменные
age = 20hasTicket = true

То же выражение со скобками читается без всякой лестницы. При составном условии – с &&, || или арифметикой внутри – скобки вокруг него ставят ради читателя:

parens.jsjs
const age = 20;const hasTicket = true;const answer = (age >= 6 + 12 && hasTicket) ? 'пускаем' : 'нельзя';console.log(answer);
Консоль
пускаем
Для движка скобки ничего не меняют, но читатель сразу видит границу условия.

// теперь вы знаете

  1. Тернарный оператор имеет форму: условие ? значение1 : значение2
  2. Он возвращает одно из двух значений; для разных сценариев действий понятнее if..else
  3. Тернарник – выражение: его результат можно сохранить, передать аргументом или вставить в шаблонную строку
  4. Вложенность и длинные ветви ухудшают читаемость – тогда лучше вернуться к if..else
  5. Приоритет определяет группировку частей выражения: арифметика выше сравнений, сравнения выше логики
  6. Если порядок трудно увидеть сразу, расставьте поясняющие скобки

Проверьте себя

Что выведет код?

const answer = 5 % 2 === 0
? 'чёт'
: 'нечет';
console.log(answer);

Можно ли использовать if..else как значение справа от =?

переключают уроки · не работают при фокусе в полях ввода