Условия и отбор карточки
Выбор темы пока лишь менял сообщение в консоли. Чтобы он влиял на каталог, программа должна решить, подходит ли конкретный курс. Такое решение выражается логическим значением, а условная инструкция определяет, какое действие выполнить после него.
Продолжим пример первой карточки. Тема курса — frontend; выбор читателя может быть all, frontend или publishing. Сначала рассмотрим каждый вариант для одной карточки. Обход нескольких курсов появится в следующем уроке, поэтому сейчас важнее правильно сформулировать правило, чем получить красивый список.
Сравнение и ветвление
Замените app.js полным примером:
const title = 'Современный JavaScript';
const topic = 'frontend';
const selectedTopic = 'publishing';
if (topic === selectedTopic) {
console.log(`Показываем: ${title}`);
} else {
console.log(`Пропускаем: ${title}`);
}
Ожидается сообщение Пропускаем: Современный JavaScript. Сравнение === спрашивает, равны ли значения без неявного приведения разных типов. Здесь обе стороны являются строками, а их содержание различается. Инструкция if выбирает первый блок при истинном результате; else задаёт действие для другого случая.
Измените выбор на frontend. Теперь ожидается сообщение о показе. Исходная тема курса при этом остаётся прежней: меняется состояние отбора. Так мы проверяем условие на двух ветках и убеждаемся, что оно не зависит от заранее записанного текста сообщения.
Не путайте сравнение с присваиванием. Один знак = меняет значение слева, три знака === сравнивают. Присваивание внутри условия иногда является допустимым JavaScript, но в этом упражнении изменит данные вместо проверки. Если слева const, такая попытка дополнительно вызовет ошибку переназначения. Сохраните явное сравнение, чтобы смысл выражения был понятен при чтении.
Несколько условий одного правила
Вариант all должен показывать карточку любой темы. Для него недостаточно сравнивать тему курса с выбранной строкой: ни одна карточка не имеет настоящую тему all. Нужны два возможных основания для показа.
const title = 'Современный JavaScript';
const topic = 'frontend';
const selectedTopic = 'all';
const matchesTopic = selectedTopic === 'all' || topic === selectedTopic;
if (matchesTopic) {
console.log(`Показываем: ${title}`);
} else {
console.log(`Пропускаем: ${title}`);
}
Оператор || выражает логическое «или». Здесь обе части сравнения дают boolean, поэтому matchesTopic также содержит boolean. При выбранной теме all первое сравнение истинно, и курс подходит независимо от второго. При выбранной теме publishing первая часть ложна, поэтому решение зависит от совпадения темы карточки.
Заметим существенную деталь: в общем случае || возвращает один из операндов, а не обязательно значение true или false. Наш результат логический потому, что сравнения уже являются логическими. Такое ограничение объяснения поможет позднее отличить условие от выбора запасного значения строки.
Для совместного выполнения двух требований используют &&. Например, курс должен соответствовать теме и иметь больше нуля уроков. Отрицание ! меняет истинность условия. Не складывайте слишком много разных проверок в одну длинную строку: названия matchesTopic и hasLessons часто делают правило понятнее, чем цепочка знаков.
Семантика ветвления и истинности рассматривается в MDN об if…else; логические операции имеют собственные правила вычисления.
Истинность и пустой ввод
if может принять любое выражение и преобразовать его результат к логическому смыслу. Это удобно, но требует осторожности с формой. Непустая строка 'false' считается истинной, несмотря на своё текстовое содержание. Поэтому строковые коды состояния нельзя использовать как готовые boolean.
const publishedFromForm = 'false';
console.log(Boolean(publishedFromForm));
console.log(publishedFromForm === 'true');
Ожидаются true и false. Первое выражение проверяет, пустая ли строка в смысле преобразования; второе сравнивает её с конкретным допустимым кодом. Для реальной схемы нужно заранее решить, какие значения принимает поле. Нельзя позволить произвольному тексту незаметно стать разрешением публикации.
Похожая ошибка возникает при запасном значении через ||. Число ноль будет заменено запасным числом, если использовать entered || 10. Для количества уроков ноль у нас запрещён, но это предметное правило, а не универсальное поведение всех форм. Когда отсутствуют именно null и undefined, применяется ??; различие разберём при деструктуризации.
Проверка границ
У нашего правила сейчас три известные темы выбора. Для ручного наблюдения последовательно задайте all, frontend, publishing. Ожидается показ, показ и пропуск. Затем задайте неизвестную строку backend: карточка будет пропущена. Само условие не проверяет, разрешён ли такой код интерфейсу; оно лишь вычисляет совпадение.
В итоговом проекте HTML ограничит обычный выбор вариантами select, а проверка загружаемых данных ограничит собственные темы курсов. Это разные границы. Значения страницы могут быть изменены через инструменты разработчика, поэтому наличие списка вариантов не является защитой серверной операции. Наш каталог пока вообще не выполняет такой операции.
Для короткой подписи иногда удобен условный оператор ? :. Он создаёт значение, выбирая одно из двух выражений, тогда как if задаёт ветвление инструкций. Например, matchesTopic ? 'показ' : 'пропуск' можно передать в сообщение. Не заменяйте им многострочные действия с вложенными условиями: компактность не должна скрывать последовательность.
Порядок нескольких веток
Когда вариантов становится больше двух, else if позволяет проверить следующее условие после неуспешного предыдущего. Например, можно выбирать подпись для малого, среднего и большого количества уроков. Порядок проверок имеет смысл: условие «больше десяти» поглотит число двадцать раньше, чем программа дойдёт до «больше двадцати», если записать ветки неосторожно.
В нашем отборе такое разделение не требуется. Правило совпадения темы выражается одним boolean, а названия вариантов остаются в HTML. Не добавляйте ветку под каждое название курса: при пятой карточке пришлось бы менять алгоритм вместо данных. Смысл обработки должен зависеть от свойств записи, а не от знания конкретного названия.
Для проверки полезно выбирать значения по обе стороны границы. У сравнения lessons > 12 это двенадцать и тринадцать; у сравнения темы — совпадающая и другая строка. Единственное успешное наблюдение не показывает работу отрицательной ветки. Даже без запуска можно выписать ожидаемые пары входа и решения, а затем вручную сравнить их с условием. В этой серии именно ожидаемые результаты отделяем от подтверждённых запуском.
Логические операции вычисляются слева направо с возможностью пропустить ненужную правую часть. В нашем сравнении это просто экономит вторую проверку при выборе all. Если бы справа находилось обращение к отсутствующему объекту, порядок мог бы влиять на появление ошибки. Поэтому иногда сначала проверяют существование записи, а только затем читают свойство.
Такая запись не должна скрывать обязательную схему данных. Для неизвестного входа каталога мы позже проведём ясную проверку целиком, а не будем рассчитывать, что каждое случайное условие безопасно пропустит повреждение. Короткое вычисление помогает реализовать правило, но не заменяет его описание.
Есть различие и между двумя независимыми if и парой if…else. Два условия могут выполнить оба блока, если оба истинны. Ветвление с else выбирает альтернативу после первого решения. Для нашей подписи «показываем или пропускаем» нужна именно альтернатива: курс не должен одновременно получать оба сообщения. Записав задачу словами, легче заметить неправильную организацию веток ещё до запуска.
Условие отбора получилось независимым от конкретного названия карточки. Следующий шаг — применить его к нескольким значениям и понять, как цикл повторяет действие. В дальнейшем это же правило станет аргументом функции фильтрации массива, но его предметный смысл уже определён здесь.