Классический цикл for в JavaScript
Когда заранее известно, сколько раз нужно повторить действие, на помощь приходит цикл for. Он удобно собирает в одной строке все три части счётного цикла: инициализацию, условие и шаг.
Пять итераций — пять строк вывода. Разберём заголовок цикла по частям:
let i = 0выполняется один раз, до старта цикла. Здесь мы инициализируем счётчик.i < 5проверяется перед каждой итерацией. Если результатtrue, тело цикла выполняется. Еслиfalse— цикл завершается.i++срабатывает после каждой итерации, прямо перед тем, как условие будет проверено снова.
Эти три части разделяются точкой с запятой, а не запятой. Формально все три можно опустить, но на практике так почти никто не пишет — в таких случаях обычно берут while.
Как всё это работает вместе
Чтобы окончательно уложить порядок в голове, полезно прогнать один цикл вручную:
Пошагово:
let i = 1— создаём счётчик и присваиваем ему 1.- Проверка
i <= 3— истина, выполняем тело. Выводим1. - Выполняется
i++— теперьiравно 2. - Проверка
i <= 3— истина. Выводим2. - Выполняется
i++— теперьiравно 3. - Проверка
i <= 3— истина. Выводим3. - Выполняется
i++— теперьiравно 4. - Проверка
i <= 3— ложь. Выходим из цикла.
Важный момент: шаг обновления срабатывает после тела цикла, а не до. Именно здесь чаще всего и спотыкаются.
Перебор массива по индексу в javascript
Чаще всего цикл for в javascript используют именно для того, чтобы пройтись по массиву. Счётчик при этом заодно служит и индексом:
На что здесь стоит обратить внимание:
- Индексация массивов начинается с нуля. Первый элемент лежит по индексу
0, последний — поlength - 1. - В условии мы пишем
i < fruits.length, а неi <= fruits.length. Со знаком<=цикл сделает лишнюю итерацию за пределами массива и выведетundefined. - Переменная
iобъявлена черезlet, поэтому её область видимости — только сам цикл. Снаружи её уже нет.
Если индекс вам не нужен, а важны только значения, удобнее и понятнее использовать for...of — про него будет отдельная статья.
break: досрочный выход из цикла
Оператор break мгновенно прерывает цикл. Это пригодится, когда вы уже нашли то, что искали, и нет смысла идти дальше:
Как только срабатывает break, управление передаётся за закрывающую скобку цикла. Шаг обновления не выполняется, условие повторно не проверяется — цикл просто завершается.
continue: пропускаем текущую итерацию
continue пропускает оставшуюся часть текущей итерации и сразу переходит к шагу обновления. Сам цикл продолжает работать — просто текущий проход до конца не доходит.
Чётные числа попадают на continue и проскакивают мимо console.log — печатаются только нечётные. continue удобен, когда нужно отбросить часть итераций, не заворачивая всё остальное тело цикла в if.
Шаг отличный от +1
Выражение шага — это обычное выражение, его не обязательно писать как i++. Давайте считать по двойке:
Count down:
Проход по массиву в обратном порядке — иногда это удобно, особенно когда вы удаляете элементы прямо во время итерации:
Что бы вы ни выбрали, правило одно: условие и шаг обновления должны работать в паре так, чтобы условие рано или поздно стало false. Иначе получите бесконечный цикл. Классический пример: for (let i = 0; i < 10; i--) — тут i уходит не в ту сторону, и цикл не завершится никогда.
Вложенный цикл for в JavaScript
Один for можно засунуть внутрь другого. При этом внутренний цикл целиком отрабатывает на каждой итерации внешнего.
Девять строк на выходе — три итерации внешнего цикла и по три итерации внутреннего для каждой из них. Давайте счётчикам осмысленные имена (row/col, i/j), а не пихайте везде одну и ту же переменную.
Есть один нюанс: break и continue действуют только на ближайший цикл. Выход из внутреннего цикла не останавливает внешний. Если такое поведение нужно, заведите флаг и проверяйте его во внешнем цикле, либо вынесите вложенную логику в отдельную функцию и используйте return.
Типичные ошибки
Вот на чём чаще всего спотыкаются новички:
Ошибка на единицу. Условие i <= arr.length уведёт цикл на один шаг дальше, чем нужно, а i < arr.length - 1 остановит его на шаг раньше. Канонический вариант — i < arr.length.
Забытый инкремент. Если пропустить i++ (или его аналог), счётчик не меняется и получается бесконечный цикл:
for (let i = 0; i < 10; ) {
console.log(i); // никогда не закончится
}
Использование var для счётчика. У var область видимости — функция, поэтому счётчик «утекает» за пределы цикла и может преподнести сюрпризы в замыканиях. Используйте let.
Изменение массива во время перебора. Удаление элементов сдвигает индексы, и часть элементов вы просто пропустите. Если нужно что-то удалять — идите в обратном порядке или собирайте новый массив через filter.
Когда стоит выбрать что-то другое
Классический цикл for в javascript доступен всегда, но для типовых задач есть более короткие варианты:
- Перебор массива javascript по значениям:
for (const item of array)— чище и понятнее. - Преобразование массива:
array.map(fn)возвращает новый массив. - Фильтрация:
array.filter(fn). - Сумма или свёртка:
array.reduce(fn, start). - Просто выполнить что-то для каждого элемента:
array.forEach(fn).
Классический for берите тогда, когда реально нужен индекс, хочется прервать или пропустить итерацию посреди цикла, либо нужен нестандартный шаг — например, обратный цикл for javascript или перебор через два.
Дальше: циклы while
Цикл for javascript хорош, когда диапазон известен заранее. А когда нет — когда нужно крутиться, пока не изменится какое-то условие, — удобнее взять while и do...while. Об этом в следующей части.
Часто задаваемые вопросы
Как выглядит синтаксис цикла for в JavaScript?
В скобках через точку с запятой идут три части: for (инициализация; условие; шаг) { ... }. Инициализация выполняется один раз, условие проверяется перед каждой итерацией, а шаг — после. Типичная запись: for (let i = 0; i < 10; i++) { ... }.
Как перебрать массив циклом for?
Классика — обычный for с индексом: for (let i = 0; i < arr.length; i++) { console.log(arr[i]); }. Если индекс не нужен, удобнее for...of: for (const item of arr) { ... }. А для преобразований и фильтрации обычно лучше подходят методы массивов — map и filter.
Как работают break и continue внутри цикла for?
break сразу выходит из цикла — управление передаётся коду после него. continue пропускает остаток текущей итерации, сразу выполняет шаг и снова проверяет условие. Оба оператора работают только с ближайшим внешним циклом, если не использовать метки (labels).
Почему мой цикл for крутится бесконечно?
Скорее всего, шаг не приближает условие к false. Например, for (let i = 0; i < 10; i--) зациклится навсегда: i стартует с 0 и уходит всё дальше в минус. Проверьте, что условие и шаг работают в одну сторону и значение действительно рано или поздно выйдет за границу.