# Циклы 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); } ``` Здесь: Цикл выполняется так:
  1. Начало: `i=0` выполняется один-единственный раз, при заходе в цикл.
  2. Условие: `i<3` проверяется перед каждой итерацией и при входе в цикл, если оно нарушено, то происходит выход.
  3. Тело: `alert(i)`.
  4. Шаг: `i++` выполняется после *тела* на каждой итерации, но перед проверкой условия.
  5. Идти на шаг 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(true)`. При этом он, как и любой другой цикл, может быть прерван директивой `break`. Если на данной итерации цикла делать больше ничего не надо, но полностью прекращать цикл не следует -- используют директиву `continue`. Обе этих директивы поддерживают "метки", которые ставятся перед циклом. Метки -- единственный способ для `break/continue` повлиять на выполнение внешнего цикла. Заметим, что метки не позволяют прыгнуть на произвольное место кода, в JavaScript нет такой возможности.