Тернарный оператор и приоритеты
Часто по условию нужно не выполнить разные действия, а просто выбрать одно из двух значений: текст кнопки, цену со скидкой, статус. Через if..else такой выбор выглядит громоздко:
const age = 20;let status;if (age >= 18) { status = 'взрослый';} else { status = 'ребёнок';}console.log(status);Обе ветки делают одно и то же – выбирают значение для status. При записи через if..else переменную приходится объявить заранее через let, потому что значение присваивается позже, внутри одной из веток.
Для такого выбора в JavaScript есть тернарный оператор – единственный оператор с тремя операндами: условие ? значениеЕслиИстина : значениеЕслиЛожь. Он сам возвращает выбранное значение, поэтому результат можно сразу записать в const:
const age = 20;const status = age >= 18 ? 'взрослый' : 'ребёнок';console.log(status);Читается вслух: возраст не меньше 18? тогда «взрослый», иначе «ребёнок». Разберём запись:
Запомнить знаки просто: вопросительный знак читается как вопрос, двоеточие – граница между двумя ответами. Сначала вычисляется условие; из двух ответов вычисляется только выбранный – до второго дело не доходит. По шагам:
age – число 20.Выбор значения, а не сценарий
Главное разграничение: тернарный оператор выбирает одно из двух значений, а if..else управляет разными сценариями действий. Если обе ветки только присваивают одной переменной разные значения, чаще всего лучше использовать тернарный оператор:
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:
const total = 6000;if (total >= 5000) { console.log('бесплатная доставка'); console.log('подарок в заказе');} else { console.log('доставка 300');}Тернарник – выражение
Ещё одно отличие. Тернарник – выражение: после вычисления на его месте остаётся одно значение – так же, как 2 + 2 превращается в 4. Поэтому его можно писать всюду, где ждут значение: справа от =, внутри console.log(...), в шаблонной строке. if..else в значение не превращается – он только выполняет одну из веток, поэтому справа от = его не поставить.
const items = 3;// выражение можно присвоить:const note = items > 0 ? 'корзина не пуста' : 'корзина пуста';// и передать аргументом:console.log(items > 0 ? 'корзина не пуста' : 'корзина пуста');// if – инструкция, у неё значения нет; так написать нельзя:// const note = if (items > 0) { ... } else { ... }Особенно это удобно в шаблонной строке: тернарник пишут прямо внутри ${...}, и подпись собирается на месте:
const score = 85;console.log(`Результат: ${score >= 60 ? 'зачёт' : 'пересдача'}, баллов: ${score}`);Когда тернарник мешает
Тернарник хорош ровно на один вопрос. Появился второй – возникает вложенность, и строку приходится распутывать. Та же логика цепочкой 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 (isLate) { console.log('Вы опоздали');} else { console.log('Вы вовремя');}isLate ? console.log('Вы опоздали') : console.log('Вы вовремя');У каждого оператора есть приоритет: 2 + 2 * 2 – это 6, умножение сильнее. Ступени арифметики, сравнений и связок собраны в уроке «Логические операторы: ||, && и !» – тернарник и присваивание встают почти в самом хвосте той же лестницы.
В полной таблице на MDN – почти два десятка уровней, и заучивать её не нужно. Рабочее правило: очевидное – без скобок, всё остальное – со скобками. Скобки бесплатны, а читают код чаще, чем пишут.
Как лестница работает на практике – одно выражение, пять ступеней:
age – 20, в hasTicket – true.То же выражение со скобками читается без всякой лестницы. При составном условии – с &&, || или арифметикой внутри – скобки вокруг него ставят ради читателя:
const age = 20;const hasTicket = true;const answer = (age >= 6 + 12 && hasTicket) ? 'пускаем' : 'нельзя';console.log(answer);// теперь вы знаете
- Тернарный оператор имеет форму: условие ? значение1 : значение2
- Он возвращает одно из двух значений; для разных сценариев действий понятнее if..else
- Тернарник – выражение: его результат можно сохранить, передать аргументом или вставить в шаблонную строку
- Вложенность и длинные ветви ухудшают читаемость – тогда лучше вернуться к if..else
- Приоритет определяет группировку частей выражения: арифметика выше сравнений, сравнения выше логики
- Если порядок трудно увидеть сразу, расставьте поясняющие скобки
Что выведет код?
const answer = 5 % 2 === 0? 'чёт': 'нечет';console.log(answer);
Можно ли использовать if..else как значение справа от =?