Нам часто приходится повторять действия.
Например, выводить товары из списка друг за другом или просто запускать один и тот же код для каждого числа от 1 до 10.
Циклы — это способ повторения одного и того же кода несколько раз.
Циклы for…of и for…in
Небольшой анонс для продвинутых читателей.
В этой статье рассматриваются только базовые циклы: while , do..while и for(..;..;..) .
Если вы пришли к этой статье в поисках других типов циклов, вот указатели:
См.…in для перебора свойств объекта.
См. «…of» и «iterables» для циклического перебора массивов и итерируемых объектов.
В противном случае, пожалуйста, читайте дальше.
Цикл while имеет следующий синтаксис:
пока (условие) {
// код
// так называемое «тело цикла»
} Пока condition истинно, code из тела цикла выполняется.
Например, приведенный ниже цикл выводит i while i < 3 :
пусть я = 0;
while (i < 3) { // показывает 0, затем 1, затем 2
предупреждение(я);
я++;
}Однократное выполнение тела цикла называется итерацией . Цикл в приведенном выше примере выполняет три итерации.
Если бы i++ отсутствовал в приведенном выше примере, цикл повторялся бы (теоретически) вечно. На практике браузер предоставляет способы остановить такие циклы, а в серверном JavaScript мы можем завершить этот процесс.
Условием цикла может быть любое выражение или переменная, а не только сравнение: условие оценивается и преобразуется в логическое значение с помощью while .
Например, более короткий способ записи while (i != 0) — while (i) :
пусть я = 3;
while (i) { // когда i становится 0, условие становится ложным и цикл останавливается
предупреждение(я);
я--;
}Фигурные скобки не требуются для однострочного тела.
Если тело цикла содержит один оператор, мы можем опустить фигурные скобки {…} :
пусть я = 3; в то время как (i) alert(i--);
Проверку условия можно переместить ниже тела цикла, используя синтаксис do..while :
делать {
// тело цикла
} Пока (условие);Цикл сначала выполнит тело, затем проверит условие и, пока оно истинно, выполнит его снова и снова.
Например:
пусть я = 0;
делать {
предупреждение(я);
я++;
} Пока (я <3); Эту форму синтаксиса следует использовать только в том случае, если вы хотите, чтобы тело цикла выполнялось хотя бы один раз, независимо от того, является ли условие истинным. Обычно предпочтительна другая форма: while(…) {…} .
Цикл for более сложен, но он также является наиболее часто используемым циклом.
Это выглядит так:
для (начало; условие; шаг) {
// ... тело цикла ...
} Давайте узнаем значение этих частей на примере. Цикл ниже запускает alert(i) для i от 0 до (но не включая) 3 :
for (let i = 0; i < 3; i++) { // показывает 0, затем 1, затем 2
предупреждение (я);
} Давайте рассмотрим оператор for по частям:
| часть | ||
|---|---|---|
| начинать | let i = 0 | Выполняется один раз при входе в цикл. |
| состояние | i < 3 | Проверяется перед каждой итерацией цикла. Если false, цикл останавливается. |
| тело | alert(i) | Выполняется снова и снова, пока условие истинно. |
| шаг | i++ | Выполняется после тела на каждой итерации. |
Общий алгоритм цикла работает следующим образом:
Начало запуска → (если условие → тело выполнения и шаг выполнения) → (если условие → тело выполнения и шаг выполнения) → (если условие → тело выполнения и шаг выполнения) → ...
То есть, begin выполняется один раз, а затем повторяется: после каждой проверки condition выполняются body и step .
Если вы новичок в циклах, возможно, вам будет полезно вернуться к примеру и воспроизвести его выполнение шаг за шагом на листе бумаги.
Вот что именно происходит в нашем случае:
// для (пусть i = 0; i < 3; i++) alert(i)
// начало запуска
пусть я = 0
// если условие → выполнить тело и выполнить шаг
если (я <3) {предупреждение (я); я++ }
// если условие → выполнить тело и выполнить шаг
если (я <3) {предупреждение (я); я++ }
// если условие → выполнить тело и выполнить шаг
если (я <3) {предупреждение (я); я++ }
// ...закончим, потому что теперь i == 3Объявление встроенной переменной
Здесь переменная «счетчик» i объявляется прямо в цикле. Это называется «встроенным» объявлением переменной. Такие переменные видны только внутри цикла.
для (пусть я = 0; я <3; я++) {
предупреждение (я); // 0, 1, 2
}
предупреждение (я); // ошибка, нет такой переменнойВместо определения переменной мы могли бы использовать существующую:
пусть я = 0;
for (i = 0; i < 3; i++) { // используем существующую переменную
предупреждение (я); // 0, 1, 2
}
предупреждение (я); // 3, видимый, поскольку объявлен вне цикла Любую часть for можно пропустить.
Например, мы можем опустить begin , если нам не нужно ничего делать в начале цикла.
Как здесь:
пусть я = 0; // мы уже объявили и назначили
for (; i < 3; i++) { // «начать» не нужно
предупреждение(я); // 0, 1, 2
} Мы также можем удалить часть step :
пусть я = 0;
для (; я < 3;) {
предупреждение (я++);
} Это делает цикл идентичным while (i < 3) .
Фактически мы можем удалить всё, создав бесконечный цикл:
для (;;) {
// повторяется без ограничений
} Обратите внимание, что две точки for запятой ; должен присутствовать. В противном случае будет синтаксическая ошибка.
Обычно цикл завершается, когда его условие становится ложным.
Но мы можем принудительно выйти в любой момент, используя специальную директиву break .
Например, приведенный ниже цикл запрашивает у пользователя серию чисел и «обрывается», когда число не введено:
пусть сумма = 0;
в то время как (истина) {
let value = +prompt("Введите число", '');
if (!value) сломать; // (*)
сумма += значение;
}
Предупреждение('Сумма:' + сумма); Директива break активируется в строке (*) если пользователь вводит пустую строку или отменяет ввод. Он немедленно останавливает цикл, передавая управление первой строке после цикла. А именно, alert .
Комбинация «бесконечный цикл + break по необходимости» отлично подходит для ситуаций, когда состояние цикла необходимо проверять не в начале или конце цикла, а в середине или даже в нескольких местах его тела.
Директива continue — это «облегченная версия» break . Это не останавливает весь цикл. Вместо этого он останавливает текущую итерацию и заставляет цикл начать новую (если позволяет условие).
Мы можем использовать его, если закончили текущую итерацию и хотим перейти к следующей.
Цикл ниже использует continue вывода только нечетных значений:
для (пусть я = 0; я < 10; я++) {
// если true, пропускаем оставшуюся часть тела
если (i % 2 == 0) продолжить;
предупреждение (я); // 1, затем 3, 5, 7, 9
} Для четных значений i директива continue прекращает выполнение тела и передает управление следующей итерации for (со следующим номером). Таким образом, alert вызывается только для нечетных значений.
Директива continue помогает уменьшить вложенность
Цикл, отображающий нечетные значения, может выглядеть так:
для (пусть я = 0; я < 10; я++) {
если (я % 2) {
предупреждение(я);
}
} С технической точки зрения это идентично приведенному выше примеру. Конечно, мы можем просто обернуть код в блок if вместо использования continue .
Но в качестве побочного эффекта это создало еще один уровень вложенности (вызов alert внутри фигурных скобок). Если код внутри if длиннее нескольких строк, это может ухудшить общую читабельность.
Без break/continue вправо от '?'
Обратите внимание, что синтаксические конструкции, не являющиеся выражениями, не могут использоваться с тернарным оператором ? . В частности, здесь не разрешены такие директивы, как break/continue .
Например, если мы возьмем этот код:
если (я > 5) {
предупреждение (я);
} еще {
продолжать;
}…и перепишите его, используя вопросительный знак:
(я > 5) ? предупреждение(i): продолжить; // продолжение здесь запрещено
…перестает работать: синтаксическая ошибка.
Это еще одна причина не использовать оператор вопросительного знака ? вместо if .
Иногда нам нужно выйти из нескольких вложенных циклов одновременно.
Например, в приведенном ниже коде мы перебираем i и j , запрашивая координаты (i, j) от (0,0) до (2,2) :
для (пусть я = 0; я <3; я++) {
for (пусть j = 0; j < 3; j++) {
let input = Prompt(`Значение по координатам (${i},${j})`, '');
// что, если мы захотим выйти отсюда к пункту «Готово» (ниже)?
}
}
Предупреждение('Готово!');Нам нужен способ остановить процесс, если пользователь отменяет ввод.
Обычный break после input приведет только к разрыву внутреннего цикла. Этого недостаточно – этикетки, приходите на помощь!
Метка — это идентификатор с двоеточием перед циклом:
labelName: для (...) {
...
} Оператор break <labelName> в приведенном ниже цикле переходит к метке:
внешний: for (пусть я = 0; я <3; я++) {
for (пусть j = 0; j < 3; j++) {
let input = Prompt(`Значение по координатам (${i},${j})`, '');
// если пустая строка или отменена, то выходим из обоих циклов
if (!input) сломать внешний; // (*)
// делаем что-то со значением...
}
}
Предупреждение('Готово!'); В приведенном выше коде команда break outer ищет метку с именем outer вверху и выходит из этого цикла.
Таким образом, элемент управления переходит прямо от (*) к alert('Done!') .
Мы также можем переместить метку на отдельную строку:
внешний:
for (пусть i = 0; i < 3; i++) { ... } Директиву continue также можно использовать с меткой. В этом случае выполнение кода переходит к следующей итерации помеченного цикла.
Этикетки не позволяют никуда «прыгнуть»
Метки не позволяют нам перейти в произвольное место кода.
Например, невозможно сделать следующее:
сломать этикетку; // переход к метке ниже (не работает) этикетка: для (...)
Директива break должна находиться внутри блока кода. Технически подойдет любой помеченный блок кода, например:
этикетка: {
// ...
сломать этикетку; // работает
// ...
} …Хотя, как мы видели в примерах выше, 99,9% времени break используется внутри циклов.
continue возможно только внутри цикла.
Мы рассмотрели 3 типа циклов:
while – условие проверяется перед каждой итерацией.
do..while – условие проверяется после каждой итерации.
for (;;) — условие проверяется перед каждой итерацией, доступны дополнительные настройки.
Для создания «бесконечного» цикла обычно используется конструкция while(true) . Такой цикл, как и любой другой, можно остановить с помощью директивы break .
Если мы не хотим ничего делать в текущей итерации и хотим перейти к следующей, мы можем использовать директиву continue .
break/continue поддержки меток перед циклом. Метка — это единственный способ для break/continue выйти из вложенного цикла и перейти к внешнему.
важность: 3
Каково последнее значение, оповещаемое этим кодом? Почему?
пусть я = 3;
в то время как (i) {
Предупреждение(я--);
}
Ответ: 1 .
пусть я = 3;
в то время как (i) {
предупреждение( я--);
} Каждая итерация цикла уменьшает i на 1 . Проверка while(i) останавливает цикл, когда i = 0 .
Следовательно, шаги цикла образуют следующую последовательность («развернутый цикл»):
пусть я = 3; предупреждение(я--); // показывает 3, уменьшает i до 2 alert(i--) // показывает 2, уменьшает i до 1 alert(i--) // показывает 1, уменьшает i до 0 // выполнено, проверка while(i) останавливает цикл
важность: 4
Для каждой итерации цикла запишите, какое значение он выводит, а затем сравните его с решением.
Оба цикла alert об одних и тех же значениях или нет?
Форма префикса ++i :
пусть я = 0; while (++i <5) alert(i);
Постфиксная форма i++
пусть я = 0; while (i++ <5) alert(i);
Задача демонстрирует, как постфиксно-префиксные формы могут привести к разным результатам при использовании в сравнениях.
От 1 до 4
пусть я = 0; while (++i <5) alert(i);
Первое значение — i = 1 , поскольку ++i сначала увеличивает i , а затем возвращает новое значение. Итак, первое сравнение — 1 < 5 , а alert показывает 1 .
Затем следуют 2, 3, 4… – значения появляются одно за другим. При сравнении всегда используется увеличенное значение, поскольку перед переменной стоит ++ .
Наконец, i = 4 увеличивается до 5 , сравнение while(5 < 5) завершается неудачно, и цикл останавливается. Поэтому 5 не отображается.
От 1 до 5
пусть я = 0; while (i++ <5) alert(i);
Первое значение снова i = 1 . Постфиксная форма i++ увеличивает i а затем возвращает старое значение, поэтому при сравнении i++ < 5 будет использоваться i = 0 (в отличие от ++i < 5 ).
Но alert звонок отдельный. Это еще один оператор, который выполняется после приращения и сравнения. Таким образом, он получает текущий i = 1 .
Затем следуйте 2, 3, 4…
Остановимся на i = 4 . Форма префикса ++i будет увеличивать его и использовать 5 при сравнении. Но здесь мы имеем постфиксную форму i++ . Таким образом, он увеличивает i до 5 , но возвращает старое значение. Следовательно, на самом деле сравнение while(4 < 5) – true, и элемент управления продолжает выдавать alert .
Значение i = 5 является последним, поскольку на следующем шаге while(5 < 5) будет ложным.
важность: 4
Для каждого цикла запишите, какие значения он будет показывать. Затем сравните с ответом.
Оба цикла alert одинаковых значениях или нет?
Постфиксная форма:
for (пусть i = 0; i < 5; i++) alert( i );
Форма префикса:
for (пусть i = 0; i < 5; ++i) alert( i );
Ответ: от 0 до 4 в обоих случаях.
for (пусть i = 0; i < 5; ++i) alert( i ); for (пусть i = 0; i < 5; i++) alert( i );
Это можно легко вывести из алгоритма for :
Выполнить один раз i = 0 перед всем (начать).
Проверьте условие i < 5
Если true – выполнить тело цикла alert(i) , а затем i++
Приращение i++ отделено от проверки условия (2). Это просто еще одно заявление.
Значение, возвращаемое приращением, здесь не используется, поэтому нет никакой разницы между i++ и ++i .
важность: 5
Используйте цикл for для вывода четных чисел от 2 до 10 .
Запустить демо-версию
for (пусть я = 2; я <= 10; я++) {
если (я % 2 == 0) {
предупреждение(я);
}
} Мы используем оператор «по модулю» % , чтобы получить остаток и проверить здесь четность.
важность: 5
Перепишите код, изменив цикл for на while не меняя его поведения (выходные данные должны остаться прежними).
для (пусть я = 0; я <3; я++) {
alert(`номер ${i}!`);
}
пусть я = 0;
в то время как (я <3) {
alert(`номер ${i}!`);
я++;
}важность: 5
Напишите цикл, который запрашивает число больше 100 . Если посетитель вводит другой номер – попросите его ввести еще раз.
Цикл должен запрашивать число до тех пор, пока посетитель не введет число больше 100 или не отменит ввод/введет пустую строку.
Здесь можно предположить, что посетитель вводит только цифры. В этой задаче нет необходимости реализовывать специальную обработку нечисловых входных данных.
Запустить демо-версию
пусть число;
делать {
num = Prompt("Введите число больше 100?", 0);
} while (num <= 100 && num); Цикл do..while .. while повторяется, пока обе проверки правдивы:
Проверка на num <= 100 – то есть введенное значение все еще не больше 100 .
Проверка && num является ложной, если num имеет значение null или пустую строку. Затем цикл while тоже останавливается.
PS Если num равно null , то num <= 100 имеет true , поэтому без второй проверки цикл не остановится, если пользователь нажмет кнопку ОТМЕНА. Обе проверки обязательны.
важность: 3
Целое число больше 1 называется простым, если его нельзя разделить без остатка ни на что, кроме 1 и самого себя.
Другими словами, n > 1 является простым числом, если его нельзя разделить без остатка ни на что, кроме 1 и n .
Например, 5 — простое число, потому что его нельзя разделить без остатка на 2 , 3 и 4 .
Напишите код, который выводит простые числа в интервале от 2 до n .
Для n = 10 результатом будет 2,3,5,7 .
PS Код должен работать для любого n , а не жестко настраиваться под какое-либо фиксированное значение.
Существует множество алгоритмов решения этой задачи.
Давайте воспользуемся вложенным циклом:
Для каждого i в интервале {
проверьте, есть ли у меня делитель от 1..i
если да => значение не является простым
если нет => значение является простым, покажите его
}Код с использованием метки:
пусть n = 10;
следующийПрайм:
for (let i = 2; i <= n; i++) { // для каждого i...
for (let j = 2; j < i; j++) { // ищем делитель..
если (i % j == 0) продолжить nextPrime; // не простое число, идем дальше i
}
предупреждение(я); // простое число
} Есть много возможностей для оптимизации. Например, мы могли бы искать делители от 2 до квадратного корня из i . Но в любом случае, если мы хотим быть действительно эффективными для больших интервалов, нам нужно изменить подход и полагаться на сложную математику и сложные алгоритмы, такие как квадратичное решето, решето общего числового поля и т. д.