Learn JavaScript #8. Циклы while и for.
При написании скриптов зачастую встаёт задача сделать однотипное действие много раз.
Например, вывести товары из списка один за другим, либо просто перебрать все числа от 1 до 10 и для каждого выполнить одинаковый код.
Для многократного повторения одного участка кода предусмотрены циклы.
Цикл "While"
Цикл while имеет следующий синтаксис:
while (condition) { // код // также называймый "телом цикла" }
Код из тела выполняется, пока условие condition
истинно.
Например, цикл ниже выводит i,
пока i < 3
:
let i = 0; while (i < 3) { // выводит 0, затем 1, затем 2 alert( i ); i++; }
Одно выполнения тела цикла по-научному называется итерация. Цикл в примере выше совершает три итерации.
Если бы строка i+=
отсутствовала в примере выше, то цикл бы повторялся (в теории) вечно. На практике, конечно, браузер не позволит такому случиться, он предоставит пользователю возможность остановить "подвисший" скрипт, а JavaScript на стороне сервера придётся "убить" процесс.
Цикл "for"
Более сложный, но при этом самый распространённый цикл - цикл for
.
for (начало; условие; шаг) { // ... тело цикла ... }
Давайте разберёмся, что означает каждая часть, на примере. Цикл ниже выполняет alert(i)
для i
от 0
до (но не включая) 3
:
let i = 0 for (i = 0; i < 3; i++) { // выведет 0, затем 1, затем 2 alert(i); }
Рассмотрим конструкцию for подробней:
- Начало -
let i = 0
- Выполняется один раз при входе в цикл - Условие -
i < 3
- Проверяется перед каждой итерацией цикла, если оно вычислится вfalse
, цикл остановится. - Тело -
alert(i)
- Выполняется снова и снова, пока условие вычисляется вtrue
. - Шаг -
i++
- Выполняется после тела цикла на каждой итерации перед проверкой условий.
Прерывание цикла: "break"
Обычно цикл завершается при вычислении условия в false
.
Но мы можем выйти из цикла в любой момент с помощью специальной директивы break
.
Например, следующий код подсчитывает сумму вводимых чисел до тех пор, пока посетитель их вводит, а затем - выдаёт:
let sum = 0; while (true) { let value = +prompt('Введите число', ''); if (!value) break; // (*) sum += value; } alert( 'Сумма: ' + sum );
Директива break
в строке (*)
полностью прекращает выполнение цикла и передаёт управление на строку за его телом, то есть на alert
.
Вообще, сочетание "бесконечный цикл + break"
- отличная штука для тех ситуаций, когда условие, по которому нужно прерваться, находится не в начале или конце цикла, а посередине или даже в нескольких местах его тела.
Переход к следующей итерации: countinue
Директива continue
- "облегчённая версия" break
. При её выполнении цикл не прерывается, а переходит к следующей итерации (если условие всё ещё равно true
).
Её используют, если понятно, что на текущем повторе цикла делать больше нечего.
Например, цикл ниже использует continue
, чтобы выводить только нечётные значения:
let i = 0; for (i = 0, i < 10; i++) { // если true, то пропустит оставшуюся часть тела в цикле if (i % 2 == 0) continue; alert(i); // 1, затем 3, 5, 7, 9 }
Для чётных значений i
, директива continue
прекращает выполнение тела цикла и передаёт управление на следующую итерацию for
(со следующим числом). Таким образом alert
вызывается только для нечётных значений.
Метки для break/continue
Бывает, нужно выйти одновременно из нескольких уровней цикла сразу.
Например, в коде ниже мы проходимся циклами по i
и j
, запрашивая с помощью prompt
координаты (i, j)
с (0,0)
до (2,2)
:
let i = 0; let j = 0; for (i; i < 3; i++) { for (j; j < 3; J++) { let input = prompt('Значение на координатах (${i},${j})', ''); // Что если мы захотим перейти к Готово (ниже) прямо отсюда? } } alert('Готово');
${i},${j}
- Вы видите это внутри строки, разделенной символом ``?. Например, «'Меня зовут #39; {имя}»
. Это новые литеральные шаблоны JS. JS заменит ${token}
значением переменной. Это гораздо более приятный синтаксис, чем "Меня зовут" + имя.
Нам нужен способ остановить выполнение если пользователь отменит ввод.
Обычный break
после input
лишь прервёт внутренний цикл, но этого недостаточно. Достичь желаемого поведения можно с помощью меток.
Метка имеет вид идентификатора с двоеточием перед циклом:
labelName: for (...) { ... }
Вызов break <labelName>
в цикле ниже ищет ближайший внешний цикл с такой меткой и переходит в его конец.
let i = 0 let j = 0 outer: for (i; i < 3; i++) { for (j; j < 3; j++) { let input = prompt('Значение на координатах (${i},${j})', ''); // если пустая строка или Отмена, то выйти из обоих циклов if (!input) break outer; // (*) // сделать что-нибудь со значениями... } } alert('Готово!');
В примере выше это означает, что вызовом break outer
будет разорван внешний цикл до метки с именем outer
.
Таким образом управление перейдёт со строки, помеченной (*)
, к alert('Готово!')
.
Также можно размещать метку на отдельной строке:
outer: for (i; i < 3; i++) { ... }
Директива continue
также может быть использована с меткой. В этом случае управление перейдёт на следующую итерацию цикла с меткой.
Важно
Метки не позволяют "прыгнуть" куда угодно
Метки не дают возможности передавать управление в произвольное место кода.
Например, нет возможности сделать следующее:
break label; // не прыгнет к метке ниже label: for (...)
Директива break
должна находиться внутри блока кода. Технически, подойдёт любой маркированный блок кода, например:
label: { // ... break label; // работает // ... }
... Хотя в 99.9% случаев break
используется внутри циклов, как мы видели в примерах выше.
К слову, continue
возможно только внутри цикла.
Итого
while
- Проверяет условие перед каждой итерациейdo...while
- Проверяет условие после каждой итерацииfor (;;)
- Проверяет условие перед каждой итерацией, есть возможность задать дополнительные настройки
Чтобы организовать бесконечный цикл, используют конструкцию while (true)
. При этом он, как и любой другой цикл, может быть прерван директивой break.
Если на данной итерации цикла делать больше ничего не надо, но полностью прекращать цикл не следует - используйте директиву continue
.
Обе этих директивы поддерживают метки, которые ставятся перед циклом. Метки - единственный способ для break/continue
выйти за пределы текущего цикла, повлиять на выполнение внешнего.
Заметим, что метки не позволяют прыгнуть в произвольное место кода, в JS нет такой возможности.
Задачи
Последнее значение цикла
Какое последнее значение выведет этот код? Почему?
let i = 3; while (i) { alert ( i-- ); } // Последнее значение будет 1, так как while (i) это while (i = 0)
Какие значения выведет цикл while?
Для каждого цикла запишите, какие значения он выведет. Потом сравните с ответом.
let i = 0; while (++i < 5) alert( i ); // 1, 2, 3, 4, потому что ++i возвращает значение после увеличение
let i = 0; while (i++ < 5) alert( i ); // 0, 1, 2, 3, 4, потому что i++ возвращает значение, а затем увеличивает // Неправильный ответ
Ответ оказался неправильным во втором варианте, потому что первым значением будет i = 1
из-за того, что в сравнении будет участвовать старое значение i = 0
, а выведено будет уже увеличенное, то есть 1
.
Для каждого значения сначала происходит сравнение, потом - увеличение, а затем срабатывание alert
.
При i = 4
произойдёт сравнение while (4 < 5)
и оно окажется верным, поэтому после этого сработает i++
, увеличив i
до 5
, так что значение 5
будет выведено. Оно станет последним.
Какие значения выведет цикл for?
Для каждого цикла запишите, какие значения он выведет.
for (let i = 0, i < 5; i++) alert( i );
Так как шаг i++
выполняется после тела цикла на каждой итерации перед проверкой условий, то в alert
будут выведены значения: 0, 1, 2, 3, 4
for (let i = 0; i < 5; ++i) alert( i );
Так как шаг ++i выполняется после тела цикла на каждой итерации перед проверкой условий, то в alert
будут выведены значения: 0, 1, 2, 3, 4
Выведите чётные числа
При помощи цикла for
выведите чётные числа от 2
до 10
.
let i = 2; for (i; i <= 10; i++) { if (i % 2 == 0) { alert(i); } }
Замените for на while
Перепишите код, заменив цикл for
на while
, без изменения поведения цикла.
for (let i = 0; i < 3; i++) { alert('number ${i}!'); }
let i = 0 while (i < 3) { alert('number ${i}!'); i++; }
Повторять цикл, пока ввод неверен
Напишите цикл, который предлагает prompt
ввести число большее 100
. Если посетитель ввёл другое число - попросить ввести ещё раз, и так далее.
Цикл должен спрашивать число пока либо посетитель не введёт число, большее 100
, либо нажмёт кнопку "Отмена (ESC)"
.
Предполагается, что посетитель вводит только числа.
let number; do { number = prompt('Введите число больше 100', '0') } while (number <= 100 && number);
Я знатно сгорел на этой простой задачке в 5 строк. Описывать все тяготы не буду, поэтому просто скажу, что не смог её сделать.
Вывести простые числа
Натуральное число, большее 1
, называется простым, если оно ни на что не делится, кроме себя и 1
.
Другими словами, n > 1
– простое, если при его делении на любое число кроме 1
и n
есть остаток.
Например, 5
это простое число, оно не может быть разделено без остатка на 2
, 3
и 4
.
Напишите код, который выводит все простые числа из интервала от 2
до n
.
Для n = 10
результат должен быть 2,3,5,7
.
P.S. Код также должен легко модифицироваться для любых других интервалов.
let n = 10; nextPrime: for (let i = 2; i <= n; i++) { // Для всех i... for (let j = 2; j < i; j++) { // проверить, делится ли число.. if (i % j == 0) continue nextPrime; // не подходит, берём следующее } alert( i ); // простое число }
Аналогичная ситуация и с этой задачкой, решить я её не смог. Сначала минут 10 я пытался понять, что такое простые числа(мем), а затем пол часа пытался понять, как их можно проверить. Тильт, что тут сказать...