Прерывание цикла: «break»
Обычно цикл завершается при вычислении условия в false
.
Но мы можем выйти из цикла в любой момент с помощью специальной директивы break
.
Например, следующий код подсчитывает сумму вводимых чисел до тех пор, пока посетитель их вводит, а затем – выдаёт:
let sum = 0;
while (true) {
let value = +prompt("Введите число", '');
if (!value) break; // (*)
sum += value;
}
alert( 'Сумма: ' + sum );
Директива break
в строке (*)
полностью прекращает выполнение цикла и передаёт управление на строку за его телом, то есть на alert
.
Вообще, сочетание «бесконечный цикл + break
» – отличная штука для тех ситуаций, когда условие, по которому нужно прерваться, находится не в начале или конце цикла, а посередине.
Переход к следующей итерации: continue
Директива continue
– «облегчённая версия» break
. При её выполнении цикл не прерывается, а переходит к следующей итерации (если условие все ещё равно true
).
Её используют, если понятно, что на текущем повторе цикла делать больше нечего.
Например, цикл ниже использует continue
, чтобы выводить только нечётные значения:
for (let i = 0; i < 10; i++) {
// если true, пропустить оставшуюся часть тела цикла
if (i % 2 == 0) continue;
alert(i); // 1, затем 3, 5, 7, 9
}
Для чётных значений i
, директива continue
прекращает выполнение тела цикла и передаёт управление на следующую итерацию for
(со следующим числом). Таким образом alert
вызывается только для нечётных значений.
Директива continue
позволяет избегать вложенности
Цикл, который обрабатывает только нечётные значения, мог бы выглядеть так:
for (let i = 0; i < 10; i++) {
if (i % 2) {
alert( i );
}
}
С технической точки зрения он полностью идентичен. Действительно, вместо continue
можно просто завернуть действия в блок if
.
Однако мы получили дополнительный уровень вложенности фигурных скобок. Если код внутри if
более длинный, то это ухудшает читаемость, в отличие от варианта с continue
.
Нельзя использовать break/continue
справа от оператора „?“
Обратите внимание, что эти синтаксические конструкции не являются выражениями и не могут быть использованы с тернарным оператором ?
. В частности, использование таких директив, как break/continue
, вызовет ошибку.
Например, если мы возьмём этот код:
if (i > 5) {
alert(i);
} else {
continue;
}
…и перепишем его, используя вопросительный знак:
(i > 5) ? alert(i) : continue; // continue здесь приведёт к ошибке
…то будет синтаксическая ошибка.
Это ещё один повод не использовать оператор вопросительного знака ?
вместо if
.
Метки для break/continue
Бывает, нужно выйти одновременно из нескольких уровней цикла сразу.
Например, в коде ниже мы проходимся циклами по i
и j
, запрашивая с помощью prompt
координаты (i, j)
с (0,0)
до (2,2)
:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Значение на координатах (${i},${j})`, '');
// Что если мы захотим перейти к Готово (ниже) прямо отсюда?
}
}
alert('Готово!');
Нам нужен способ остановить выполнение если пользователь отменит ввод.
Обычный break
после input
лишь прервёт внутренний цикл, но этого недостаточно. Достичь желаемого поведения можно с помощью меток.
Метка имеет вид идентификатора с двоеточием перед циклом:
labelName: for (...) {
...
}
Вызов break <labelName>
в цикле ниже ищет ближайший внешний цикл с такой меткой и переходит в его конец.
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Значение на координатах (${i},${j})`, '');
// если пустая строка или Отмена, то выйти из обоих циклов
if (!input) break outer; // (*)
// сделать что-нибудь со значениями...
}
}
alert('Готово!');
В примере выше это означает, что вызовом break outer
будет разорван внешний цикл до метки с именем outer
, и управление перейдёт со строки, помеченной (*)
, к alert('Готово!')
.
Можно размещать метку на отдельной строке:
outer:
for (let i = 0; i < 3; i++) { ... }
Директива continue
также может быть использована с меткой. В этом случае управление перейдёт на следующую итерацию цикла с меткой.
Метки не позволяют «прыгнуть» куда угодно
Метки не дают возможности передавать управление в произвольное место кода.
Например, нет возможности сделать следующее:
break label; // не прыгает к метке ниже
label: for (...)
Вызов break/continue
возможен только внутри цикла, и метка должна находиться где-то выше этой директивы.
Итого
Мы рассмотрели 3 вида циклов:
while
– Проверяет условие перед каждой итерацией.do..while
– Проверяет условие после каждой итерации.for (;;)
– Проверяет условие перед каждой итерацией, есть возможность задать дополнительные настройки.
Чтобы организовать бесконечный цикл, используют конструкцию while (true)
. При этом он, как и любой другой цикл, может быть прерван директивой break
.
Если на данной итерации цикла делать больше ничего не надо, но полностью прекращать цикл не следует – используют директиву continue
.
Обе этих директивы поддерживают метки, которые ставятся перед циклом. Метки – единственный способ для break/continue
выйти за пределы текущего цикла, повлиять на выполнение внешнего.
Заметим, что метки не позволяют прыгнуть в произвольное место кода, в JavaScript нет такой возможности.