Логические операторы: &&, || и ??
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Операторы &&, || и ?? в JavaScript устроены хитрее, чем кажется: они не отвечают «да» или «нет», а возвращают одно из значений, которые в них передали, — и часто даже не смотрят на правую половину выражения. Разберёмся, как это работает и почему привычное x || 100 однажды съест ваш ноль.
Кажется, что || и && отвечают true или false. На самом деле они возвращают одно из переданных значений. a || b — «первое истинное, а если истинных нет, то последнее»; a && b — «первое ложное, а если ложных нет, то последнее».
Операнды вычисляются слева направо, и вычисление обрывается, как только ответ ясен. Это и есть короткое замыкание: если левая часть || уже истинна, правая не выполняется вообще — со всеми вызовами функций, которые в ней были.
Из правила «первое истинное» вырос популярный приём: значение || запасной вариант. Если слева пусто, подставится запасное.
Беда в том, что || спрашивает не «есть ли значение», а «истинно ли оно». Ложными считаются 0, пустая строка, null, undefined, NaN и false. Все они будут заменены запасным вариантом — даже если пользователь выбрал их осознанно.
Подсказка на пальцах: || спрашивает «есть ли тут что-то полезное?», а ?? — «есть ли тут хоть что-нибудь?»
?? — оператор нулевого слияния. Он подставляет правый операнд, только если слева null или undefined. Ноль, пустая строка и false для него — обычные данные, которые трогать не надо.
Правило выбора простое. Если 0 или пустая строка — допустимые значения (ширина, количество, комментарий), берите ??. Если любое «пустое» значение нужно заменить — например, пустое имя на «Гость», — || здесь уместнее.
Запись a ?? b || c двусмысленна: непонятно, что считать раньше. Авторы языка решили не угадывать за программиста и запретили такое сочетание на уровне синтаксиса — движок бросит SyntaxError ещё до запуска. Лечится скобками.
! делает две вещи сразу: приводит значение к булеву типу и переворачивает его. Поэтому !0 — это true. Двойное !! переворачивает дважды, то есть просто отвечает на вопрос «истинно ли это?» — ручной аналог Boolean(x).
| Значение | Boolean(значение) |
|---|---|
| 0 | false |
| пустая строка "" | false |
| null | false |
| undefined | false |
| NaN | false |
| строка "0" | true |
| [] — пустой массив | true |
| {} — пустой объект | true |
Ложных значений в языке всего восемь, и полный их список с разбором — в статье «Условия: if, else и тернарный оператор». Для || и ?? из этого следует одно: || подменит запасным вариантом любое из восьми, а ?? — только null и undefined.
Когда операторы стоят рядом, порядок такой: сначала !, затем сравнения (>, === и другие), затем &&, и в самом конце || и ??. Значит, a || b && c читается как a || (b && c), а x > 0 && y > 0 работает без скобок именно так, как ожидается.
Скобки ничего не стоят и читаются легче, чем таблица приоритетов в чужой голове. Если выражение длиннее двух операторов — ставьте их, даже когда язык позволяет обойтись.
Есть логические операторы присваивания. x ??= y присвоит y, только если x равен null или undefined; x ||= y — если x ложный. Они тоже ленивые: правая часть не вычисляется, когда присваивать не нужно.
- || и && возвращают один из операндов, а не true/false, и не вычисляют правую часть, если ответ уже понятен по левой.
- Приём «значение || запасное» подводит на 0, пустой строке и false: они ложные, хотя и осмысленные.
- ?? подставляет запасное значение только вместо null и undefined; смешивать его с || и && без скобок запрещено синтаксисом.
- !x приводит к булеву типу с переворотом, !!x — просто приведение, то же самое, что Boolean(x).
- Порядок вычисления: !, затем сравнения, затем &&, затем || и ??. Сомневаетесь — ставьте скобки.
1/3Что выведет console.log(0 || "" || "хвост");?
2/3Чем ?? отличается от ||?
3/3Что выведет console.log(false || 0 && 1);?
Верните ноль на место
Функция подставляет значения по умолчанию, но теряет осознанный ноль и пустую строку. Почините её так, чтобы запасное значение появлялось только когда параметра действительно нет.
Достаточно поменять || на ??: ноль и пустая строка — законные значения, а не «пустота».
Если бы по замыслу пустое имя нужно было всё-таки заменять на «Блок», то для label правильнее оставить || — выбор оператора зависит от того, считаете вы пустую строку данными или отсутствием данных.
Предскажите вывод
Не запуская код, предположите, что выведет каждая строка, и только потом проверьте себя в консоли.
В первой строке истинных операндов нет, поэтому || возвращает последний — 0.
Во второй первый ?? отдаёт 0, а ноль — не пустота, поэтому второй ?? его и оставляет.
В третьей строка "0" непустая, значит истинная: содержимое роли не играет.
В четвёртой && ищет первое ложное значение, находит 0 и до 3 уже не доходит.