Перейти к содержанию

Циклы и обход учебных данных

Каталог содержит четыре курса. Повторять отдельный вызов вывода для каждой карточки неудобно: после добавления пятого курса понадобится найти несколько участков программы и синхронно изменить их. Цикл выполняет одно действие для последовательности значений и позволяет хранить правило обработки в одном месте.

В этом уроке используем только названия и количества уроков. Подробное устройство массива рассмотрим дальше, а сейчас достаточно знать, что квадратные скобки создают упорядоченный набор. Наши четыре значения соответствуют постоянным карточкам серии: JavaScript, HTML и CSS, производительность и Markdown.

Один проход по значениям

Замените app.js полным примером:

const titles = [
  'Современный JavaScript',
  'HTML и CSS',
  'Производительность сайта',
  'Статический сайт из Markdown',
];
for (const title of titles) {
  console.log(title);
}

Ожидаются четыре названия в исходном порядке. Конструкция for…of получает очередное значение массива, связывает его с именем title и выполняет тело. Затем выполняется следующая итерация — следующий проход тела цикла. Для этого примера порядок данных и порядок сообщений совпадают.

Объявление const title находится внутри механизма цикла. Мы не переназначаем одну постоянную переменную вручную: каждая итерация получает свою связь для текущего значения. После цикла имя title не предназначено для чтения. Если нужно итоговое значение для всей последовательности, объявим отдельную переменную во внешней области.

Нельзя перепутать for…of и for…in. Второй механизм перечисляет имена свойств и не является выбранным способом обхода значений массива. В маленьком массиве числа индексов могут выглядеть правдоподобно, но это другое действие. Наиболее прямое выражение нашей задачи — получить название, а не имя свойства.

Накопление суммы

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

const lessonCounts = [20, 16, 12, 12];
let totalLessons = 0;
for (const count of lessonCounts) {
  totalLessons += count;
}
console.log(totalLessons);

Ожидается число 60. Запись += прибавляет текущее значение к прежней сумме и сохраняет новый результат. После первой итерации сумма равна двадцати, после второй — тридцати шести, затем сорока восьми и шестидесяти. Начальный ноль важен: он обозначает сумму до обработки каких-либо элементов.

Если объявить totalLessons внутри тела, при каждой итерации значение снова начнётся с нуля, а после цикла переменная окажется недоступной. Это практическое применение области видимости прошлого урока. Переменная всего результата относится к операции обхода целиком; временное название текущего элемента относится к одной итерации.

Сами массивы titles и lessonCounts пока раздельны только ради знакомства с механизмами. В реальном каталоге такая организация хрупкая: удаление названия без удаления количества разрушит соответствие. В уроке об объектах объединим связанные свойства в одну запись, чтобы обходить курс, а не несколько параллельных списков.

Индекс и условие продолжения

Иногда нужен номер карточки. Рассмотрим другой вид цикла на прежних названиях:

const titles = ['Современный JavaScript', 'HTML и CSS',
  'Производительность сайта', 'Статический сайт из Markdown'];
for (let index = 0; index < titles.length; index += 1) {
  console.log(`${index + 1}. ${titles[index]}`);
}

Ожидаемая нумерация начинается с единицы, хотя доступ к элементам начинается с индекса ноль. Три части заголовка цикла задают начало, условие продолжения и изменение после итерации. titles.length равен четырём. Последний допустимый индекс равен трём, поэтому сравнение строгое: index < titles.length.

При index <= titles.length добавится лишний проход с индексом четыре. Значение titles[4] для этого массива отсутствует и даст undefined. Такая ошибка часто видна только на последней строке результата. Не исправляйте её специальным скрытием пустой карточки: верните правильную границу обхода.

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

Основные варианты циклов, break и continue приведены в MDN о циклах. Для каталога чаще пригодится простой обход или готовый метод массива; сложность цикла выбирается по задаче.

Пропуск и завершение

continue завершает текущую итерацию и переходит к следующей. Например, при обходе количеств можно пропускать значения меньше шестнадцати. break прекращает весь цикл после первого найденного подходящего значения. Эти действия различаются: одно оставляет возможность обработать следующие записи, другое исключает их обработку.

Когда понадобится показать все подходящие карточки, ранний break будет ошибочным: результат неожиданно остановится на первом курсе. Если задача — только узнать, существует ли подходящий курс, остановка может быть разумной. Сначала сформулируйте нужный результат словами, а затем выбирайте управление обходом.

Наш суммирующий цикл ничего не меняет внутри исходного массива. Это хорошая привычка для отображения: данные остаются пригодными для повторного расчёта и другого отбора. Не добавляйте и не удаляйте элементы той же коллекции во время знакомства с её обходом — последствия зависят от механизма и отвлекают от основного результата.

Пустая последовательность

При пустом массиве простой обход for…of не выполнит тело ни разу. В примере суммы останется начальный ноль. Это правильное следствие выбранной операции, но интерфейс может нуждаться в дополнительном сообщении: отсутствие курсов нельзя всегда показывать как обычный каталог с нулём уроков. Позднее различим пустой результат отбора и отказ загрузки.

Размещение вывода тоже меняет наблюдение. Если console.log(totalLessons) стоит внутри тела, появятся промежуточные суммы двадцать, тридцать шесть, сорок восемь и шестьдесят. После цикла получается только окончательная сумма. Оба варианта допустимы, однако соответствуют разным вопросам: как развивается вычисление или что оно дало в конце.

Вложенные циклы нужны, когда каждая запись действительно содержит ещё одну последовательность, например список глав курса. Для наших четырёх названий второй цикл не добавляет полезного смысла: он только повторит сообщения несколько раз. Не копируйте вложенность из чужого примера без понимания единицы обработки. В текущем контракте одна итерация внешнего обхода соответствует одному курсу, а итоговая сумма относится ко всей последовательности.

Количество итераций и количество сообщений не всегда совпадают. Внутри тела можно вывести две строки на каждый курс или пропустить вывод по условию. Поэтому четыре сообщения не являются универсальным доказательством четырёх итераций. В нашем первом листинге связь один к одному существует потому, что один вызов выполняется без дополнительного условия для каждого названия.

Если требуется номер только показанных записей, он не обязательно равен исходному индексу. После пропуска некоторых курсов исходные позиции будут иметь пробелы. В дальнейшем сначала получим отобранный массив, а затем его порядок станет порядком отображения. Так правила отбора и нумерации не будут случайно смешаны в одном счётчике.

Не меняйте коллекцию только ради того, чтобы цикл «не увидел» ненужный элемент. Условие может решить не выполнять действие для текущего значения, сохранив данные. Наш интерфейс должен позволять переключить тему обратно, поэтому любой временный пропуск является решением отображения. Именно эта модель повторится при работе с методом filter, где новый массив становится отдельным результатом.

Для самостоятельного наблюдения добавьте в конец lessonCounts значение четыре. Предскажите новую сумму и число итераций. Затем верните постоянные данные серии: в дальнейшем сумма шестидесяти станет простой контрольной величиной, помогающей заметить потерянную или лишнюю карточку. Дальше выделим форматирование сообщения в функцию, чтобы повторяемое действие имело собственное имя и параметры.

Оглавление курса