# Циклы while, for
При написании скриптов зачастую встает задача сделать однотипное действие много раз.
Например, вывести товары из списка один за другим. Или просто перебрать все числа от 1 до 10 и для каждого выполнить одинаковый код.
Для многократного повторения одного участка кода - предусмотрены *циклы*.
[cut]
## Цикл while
Цикл `while` имеет вид:
```js
while (условие) {
// код, тело цикла
}
```
Пока `условие` верно -- выполняется код из тела цикла.
Например, цикл ниже выводит `i` пока `i < 3`:
```js
//+ run
var i = 0;
while (i < 3) {
alert(i);
i++;
}
```
**Повторение цикла по-научному называется *"итерация"*. Цикл в примере выше совершает три итерации.**
Если бы `i++` в коде выше не было, то цикл выполнялся бы (в теории) вечно. На практике, браузер выведет сообщение о "зависшем" скрипте и посетитель его остановит.
**Бесконечный цикл** можно сделать и проще:
```js
while (true) {
// ...
}
```
**Условие в скобках интерпретируется как логическое значение, поэтому вместо `while (i!=0)` обычно пишут `while (i)`**:
```js
//+ run
var i = 3;
*!*
while (i) { // при i=0 значение в скобках будет false и цикл остановится
*/!*
alert(i);
i--;
}
```
## Цикл do..while
Проверку условия можно поставить *под* телом цикла, используя специальный синтаксис `do..while`:
```js
do {
// тело цикла
} while (условие);
```
Цикл, описанный, таким образом, сначала выполняет тело, а затем проверяет условие.
Например:
```js
//+ run
var i = 0;
do {
alert(i);
i++;
} while (i < 3);
```
Синтаксис `do..while` редко используется, т.к. обычный `while` нагляднее -- в нём не приходится искать глазами условие и ломать голову, почему оно проверяется именно в конце.
## Цикл for
Чаще всего применяется цикл `for`. Выглядит он так:
```js
for (начало; условие; шаг) {
// ... тело цикла ...
}
```
Пример цикла, который выполняет `alert(i)` для `i` от `0` до `2` включительно (до `3`):
```js
//+ run
var i;
for (i=0; i<3; i++) {
alert(i);
}
```
Здесь:
- **Начало:** `i=0`.
- **Условие:** `i<3`.
- **Шаг:** `i++`.
- **Тело:** `alert(i)`, т.е. код внутри фигурных скобок (они не обязательны, если только одна операция)
Цикл выполняется так:
- Начало: `i=0` выполняется один-единственный раз, при заходе в цикл.
- Условие: `i<3` проверяется перед каждой итерацией и при входе в цикл, если оно нарушено, то происходит выход.
- Тело: `alert(i)`.
- Шаг: `i++` выполняется после *тела* на каждой итерации, но перед проверкой условия.
- Идти на шаг 2.
Иными, словами, поток выполнения: `начало` -> (если `условие` -> `тело` -> `шаг`) -> (если `условие` -> `тело` -> `шаг`) -> ... и так далее, пока верно `условие`.
[smart]
В цикле также можно определить переменную:
```js
//+ run
for (*!*var*/!* i=0; i<3; i++) {
alert(i); // 0, 1, 2
}
```
Эта переменная будет видна и за границами цикла, в частности, после окончания цикла `i` станет равно `3`.
[/smart]
## Пропуск частей for
Любая часть `for` может быть пропущена.
Например, можно убрать `начало`. Цикл в примере ниже полностью идентичен приведённому выше:
```js
//+ run
var i = 0;
for (; i<3; i++) {
alert(i); // 0, 1, 2
}
```
Можно убрать и `шаг`:
```js
//+ run
var i = 0;
for (; i<3;) {
alert(i);
// цикл превратился в аналог while (i<3)
}
```
А можно и вообще убрать все, получив бесконечный цикл:
```js
for (;;) {
// будет выполняться вечно
}
```
При этом сами точки с запятой `;` обязательно должны присутствовать, иначе будет ошибка синтаксиса.
[smart header="`for..in`"]
Существует также специальная конструкция `for..in` для перебора свойств объекта.
Мы познакомимся с ней позже, когда будем [говорить об объектах](#for..in).
[/smart]
## Прерывание цикла: break
Выйти из цикла можно не только при проверке условия но и, вообще, в любой момент. Эту возможность обеспечивает директива `break`.
Например, следующий код подсчитывает сумму вводимых чисел до тех пор, пока посетитель их вводит, а затем -- выдаёт:
```js
var sum = 0;
while(true) {
var value = +prompt("Введите число", '');
*!*
if (!value) break; // (*)
*/!*
sum += value;
}
alert('Сумма: ' + sum);
```
Директива `break` в строке `(*)`, если посетитель ничего не ввёл, полностью прекращает выполнение цикла и передаёт управление на строку за его телом, то есть на `alert`.
Вообще, сочетание "бесконечный цикл + break" -- отличная штука для тех ситуаций, когда условие, по которому нужно прерваться, находится не в начале-конце цикла, а посередине.
## Следующая итерация: continue [#continue]
Директива `continue` прекращает выполнение *текущей итерации* цикла.
Она -- в некотором роде "младшая сестра" директивы `break`: прерывает не весь цикл, а только текущее выполнение его тела, как будто оно закончилось.
Её используют, если понятно, что на текущем повторе цикла делать больше нечего.
Например, цикл ниже использует `continue`, чтобы не выводить четные значения:
```js
//+ run
for (var i = 0; i < 10; i++) {
*!*if (i % 2 == 0) continue;*/!*
alert(i);
}
```
Для четных `i` срабатывает `continue`, выполнение тела прекращается и управление передается на следующий проход `for`.
[smart header="Директива `continue` позволяет обойтись без скобок"]
Цикл, который обрабатывает только нечётные значения, мог бы выглядеть так:
```js
for (var i = 0; i < 10; i++) {
if (i % 2) {
alert(i);
}
}
```
С технической точки зрения он полностью идентичен. Действительно, вместо `continue` можно просто завернуть действия в блок `if`. Однако, мы получили дополнительный уровень вложенности фигурных скобок. Если код внутри `if` более длинный, то это ухудшает читаемость, в отличие от варианта с `continue`.
[/smart]
[warn header="Нельзя использовать break/continue справа от оператора '?'"]
Обычно мы можем заменить `if` на оператор вопросительный знак `'?'`.
То есть, запись:
```js
if (условие) {
a();
} else {
b();
}
```
...Аналогична записи:
```js
условие ? a() : b();
```
В обоих случаях в зависимости от условия выполняется либо `a()` либо `b()`.
Но разница состоит в том, что оператор вопросительный знак `'?'`, использованный во второй записи, возвращает значение.
**Синтаксические конструкции, которые не возвращают значений, нельзя использовать в операторе `'?'`.**
К таким относятся большинство конструкций и, в частности, `break/continue`.
Поэтому такой код приведёт к ошибке:
```js
(i > 5) ? alert(i) : *!*continue*/!*;
```
Впрочем, как уже говорилось ранее, оператор вопросительный знак `'?'` не стоит использовать таким образом. Это -- всего лишь ещё одна причина, почему для проверки условия предпочтителен `if`.
[/warn]
## Метки для break/continue
Бывает нужно выйти одновременно из нескольких уровней цикла.
Например, внутри цикла по `i` находится цикл по `j`, и при выполнении некоторого условия мы бы хотели выйти из обоих циклов сразу:
```js
//+ run
*!*outer:*/!* for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
var input = prompt('Значение в координатах '+i+','+j, '');
// если отмена ввода или пустая строка -
// завершить оба цикла
if (!input) *!*break outer*/!*; // (*)
}
}
alert('Готово!');
```
В коде выше для этого использована *метка*.
Метка имеет вид `"имя:"`, имя должно быть уникальным. Она ставится перед циклом, вот так:
```js
outer: for (var i = 0; i < 3; i++) { ... }
```
Можно также выносить ее на отдельную строку:
```js
outer:
for (var i = 0; i < 3; i++) { ... }
```
Вызов `break outer` ищет ближайший внешний цикл с такой меткой и переходит в его конец.
В примере выше это означает, что будет разорван самый внешний цикл и управление перейдёт на `alert`.
Директива `continue` также может быть использована с меткой, в этом случае управление перепрыгнет на следующую итерацию цикла с меткой.
## Итого
JavaScript поддерживает три вида циклов:
- `while` -- проверка условия перед каждым выполнением.
- `do..while` -- проверка условия после каждого выполнения.
- `for` -- проверка условия перед каждым выполнением, а также дополнительные настройки.
Чтобы организовать бесконечный цикл, используют конструкцию `while(true)`. При этом он, как и любой другой цикл, может быть прерван директивой `break`.
Если на данной итерации цикла делать больше ничего не надо, но полностью прекращать цикл не следует -- используют директиву `continue`.
Обе этих директивы поддерживают "метки", которые ставятся перед циклом. Метки -- единственный способ для `break/continue` повлиять на выполнение внешнего цикла.
Заметим, что метки не позволяют прыгнуть на произвольное место кода, в JavaScript нет такой возможности.