Циклы в JavaScript

Назначение и виды циклов
Циклы – это простой способ для многократного выполнения одних и тех же действий (кода).
При этом однократное выполнения кода в цикле называется итерацией.
В JavaScript существуют различные виды циклов, но все они, по сути, делают одно и тоже. Просто с помощью одних циклов более просто решаются одни задачи, с помощью других – иные:
Цикл for
Данный цикл в основном используется когда известно точное количество повторений. Этот цикл ещё называют циклом со счётчиком.
Синтаксис цикла «for»:
for (инициализация; условие; финальное выражение) { /* тело цикла */ }

Основные части конструкции цикла «for»:
- инициализация - это выражение, которое выполняется один раз перед выполнением цикла; обычно используется для инициализации счётчика;
- условие - это выражение, истинность которого проверяется перед каждой итерацией; если выражение вычисляется как истина, то выполняется итерация; в противном случае цикл «for» завершает работу;
- финальное выражение - это выражение, которое выполняется в конце каждой итерации; обычно используется для изменения счетчика;
- тело цикла - инструкции, выполнение которых нужно повторять.
Рассмотрим пример цикла, который выведет в консоль числа от 1 до 8:
// цикл «for» от 1 до 8, с шагом 1 for (var i = 1; i <= 8; i++) { console.log(i); }
В этом примере:
- инициализация:
var i = 1
(объявление переменнойi
и присвоение ей значения 1); - условие выполнения цикла:
i <= 8
(пока значение переменнойi
меньше или равно 8); - финальное выражение, которое нужно выполнять в конце каждой итерации:
i++
(увеличение значение переменнойi
на 1); - инструкция, которую нужно выполнять:
console.log(i)
(выведение значения счётчика в консоль).
При этом если тело цикла состоит из одной инструкции, то её можно не заключать в фигурные скобки.
Таким образом, пример, приведённый выше, можно записать ещё так:
// цикл «for» от 1 до 8, с шагом 1 for (var i = 1; i <= 8; i++) console.log(i);
Необязательные части цикла цикла «for».
В «for» все части цикла являются не обязательными.
Например, можно пропустить выражение инициализации:
var i = 1; // цикл «for» for (; i <= 8; i++) { console.log(i); }
В этом случае инициализацию переменной можно вынести за пределы цикла.
Условие в «for» тоже является не обязательным. Без условия цикл будет выполняться бесконечное количество раз. В этом случае чтобы его прервать (выйти из цикла) необходимо использовать инструкцию break
.
// цикл «for» for (var i = 1; ; i++) { if (i >= 8) { // условие прерывания цикла break; } console.log(i); }
Финальное выражение в «for» также является не обязательным. Счётчик цикла в этом случае можно, например, изменять в теле.
// цикл «for» for (var i = 1; i <= 8; ) { console.log(i); i++; // увеличение счетчика на 1 }
В «for» можно вообще опустить 3 выражения (бесконечный цикл):
var i = 1; // цикл «for» for (;;) { if (i >= 8) { break; } console.log(i); i++; }
Кроме этого, в качестве тела цикла «for» можно использовать пустое выражение (;
). Это используется, когда вам не нужно выполнять ни одной инструкции.
Например:
var arrA = [8, 12, 24], arrB = []; for (i = 0; i < arrA.length; arrB[i] = arrA[i++] / 2) ; console.log(arrB); // [4, 6, 12]
Пустое выражение в этом случае рекомендуется дополнительно снабжать комментарием:
// сумма чисел в массиве var arr = [2, 7, 3]; for (var i = 0, length = arr.length, sum = 0; i < length; sum += arr[i++]) /* пустое выражение */ ; // выведем сумму чисел в консоль: console.log(sum); // 12
Пример использования цикла «for» для перебора элементов массива:
var arr = ["a", "b", "c"]; // массив for (var i = 0, length = arr.length; i < length; i++) { console.log(arr[i]); }
Пример, в котором выведем таблицу умножения в консоль. Для реализации этого примера будем использовать вложенные циклы.

var output = ''; for (var i = 1; i <= 9; i++) { for (var j = 1; j <= 9; j++) { output += ' ' + i * j; if (i * j < 10) { output += ' '; } } console.log(output); output = ''; }
Цикл называется вложенным, если он находится в теле другого цикла.
Цикл while
Данный цикл предназначен для многократного выполнения одних и тех же инструкций до тех пор, пока истинно некоторое условие. Цикл «while» в основном используется, когда количество повторений заранее не известно.
while (условие) { /* тело цикла */ }

Истинность условия проверяется перед каждым выполнением. Если перед первой итерацией условие ложно, то цикл не выполнится ни разу.
Пример, в котором выведем в консоль чётные числа в диапазоне от 1 до 8:
// объявим переменную а и присвоим ей значение 0 let a = 0; //цикл while с условием a <= 8 while (a <= 8) { // увеличим значение переменной a на 1 a++; // если число нечётное (остаток от деления на 2 не равен 0), то... if (a % 2 !== 0) { // пропустим дальнейшее выполнение текущей итерации и перейдём к следующей continue; } // выведем значение переменной a в консоль console.log(a); }
Цикл do...while
Цикл «do...while», также как и цикл «while», выполняет одни и те же инструкции до тех пор, пока указанное условие истинно. Но в отличие от «while» в «do...while» условие проверяется после выполнения инструкций. Поэтому цикл «do...while» в любом случае выполнится не меньше одного раза, даже если условие изначально ложно.

do { /* тело цикла */ } while (условие)
Пример, в котором выведем в консоль сумму чисел, которые будем запрашивать у пользователя с помощью функции prompt
:
// num – переменная для хранения числа, введённого пользователем // sum – переменная для хранения суммы чисел let num, sum = 0; // цикл «do...while» do { // запросим у пользователя данные и приведём их к числу num = +prompt ('Введите число', ''); // если то, что ввёл пользователь после приведения является числом, то... if (num) { // прибавим к сумме число, введённое пользователем sum += num; } // если num приводится к истине, то выполняем ещё итерацию } while (num); console.log(sum);
Цикл for...in
Цикл «for...in» предназначен для перебора перечисляемых имён свойств объекта. В JavaScript свойство является перечисляемым, если его внутренний флаг [[Enumerable]]
равен true
.
Свойства объекта, которые не относятся к перечисляемым, в цикле не участвуют.
Например, объект (массив) созданный с использованием функции-конструктора Array
или его литеральной записи имеет не перечисляемые свойства от Array.prototype
и Object.prototype
, такие как indexOf()
, some()
, toString()
и др. Они не будут участвовать в цикле.
/* цикл для перебора всех перечисляемых свойств объекта - key – переменная, в которую будет помещаться имя свойства объекта - object – объект, свойства которого нужно перебрать */ for (key in object) { /* тело цикла */ }
Переберём свойства объекта, созданного с помощью литеральной записи:
let car = { manufacturer: 'Ford', model: 'Fiesta', color: 'black' }; for (let propName in car) { // propName – имя свойства // car[propName] – значение свойства console.log(propName + ' = ' + car[propName]); } // в консоль будет выведено: manufacturer = Ford, model = Fiesta, color = black
Кроме этого, следует отметить, что цикл for...in
проходит не только по перечисляемых свойствам этого объекта, но и по наследуемым.
let item = { a: 1, b: 2 } let newItem = Object.create(item); newItem.c = 3; newItem.d = 4; for (let propName in newItem) { console.log(propName); } // в консоли будет выведено: c, d, a, b
Если вам наследуемые свойства не нужно учитывать, то их можно пропустить:
for (let propName in newItem) { // переходим к следующей итерации, если текущее свойство не принадлежит этому объекту if(!newItem.hasOwnProperty(propName)) { continue; } console.log(propName); } // в консоли будет выведено: c, d
Использование цикла for... in для перебора массива. В массиве свойствами являются числовые индексы.
// массив var arr = ["Rock", "Jazz", "Classical", "Hip Hop"]; // перебор массива с помощью цикла for in for (let index in arr) { // index - индекс элемента массива // arr[index] – значение элемента console.log(arr[index]); } // в результате в консоль будет выведено: "Rock", "Jazz", "Classical", "Hip Hop"
Цикл for...in проходит по свойствам в произвольном порядке. Поэтому если при переборе массива для вас важен порядок символов, то данный цикл лучше не использовать.
При использовании цикла for…in стоит обратить внимание на то, что если вы к массиву добавили свои пользовательские свойства, то он по ним тоже пройдётся:
var arr = [5, 7, -3]; arr.sum = 2; for (var key in arr) { console.log(arr[key]); } // в консоль будет выведено 5, 7, -3, 2
Если вам такой сценарий не нужен, то тогда для перебора массивов лучше использовать обычный цикл for.
Использование цикла for…in для перебора символов в строке:
var str = 'Метод'; for (var key in str) { console.log(str[key]); } // М, е, т, о, д
Инструкции break и continue
Внутри тела цикла можно использовать специальные инструкции: break
и continue
.
Инструкция «break» предназначена для прекращения выполнения текущего цикла. Другими словами, она осуществляет выход и передачу управления инструкции, идущей после этого цикла.
Пример, в котором завершим цикл по перебору элементов массива, если его текущий элемент не будет являться числом:
// массив var arr = [5, 3, "a", 4, "b", 16]; // цикл «for» для перебора массива arr for (var i = 0, length = arr.length; i < length; i++) { // если текущий элемент массива не является числом, то... if (typeof arr[i] !== 'number') { // прерываем выполнение цикла break; } // выводим текущий элемент массива в консоль console.log(arr[i]); } // в результате в консоль будет выведено: 5, 3
Инструкция «continue» предназначена для прекращения дальнейшего выполнения кода и перехода к следующей итерации цикла.
Пример, в котором найдём в слове «программирование» символы «а» и «о», и выведем их позиции в консоль:
// строка var str = 'программирование'; // цикл "for" для перебора символов строки for (var i = 0, length = str.length; i < length; i++) { // если текущий символ не равен а и о, то... if (str[i] !== 'а' && str[i] !== 'о') { // прекращаем выполнение текущей итерации и переходим к следующей continue; } // выведем в консоль сам символ и его индекс console.log(i + ' => ' + str[i]); } // данный цикл выведет в консоль: 2 => "о", 5 => "а", 10 => "о", 12 => "а"
Метки для break и continue
Метка представляет собой идентификатором с двоеточием, который необходимо указать перед циклом.
someLabel: while (условие) { // текло цикла }
Далее после оператора break
или continue
необходимо указать эту метку:
someLabel: while (условие) { if (условие) { break someLabel; } }
Вызов break someLabel
приведёт к переходу в конец цикла, перед которым данная метка указана.
Если метка используется с ключевым словом continue
, то в этом случае выполнение этого действия приведёт к немедленному переходу к следующей итерации цикла, перед которым данная метка указана.
В коде с одиночным циклом использование метки не даст никакого результата. Её есть смысл использовать только когда вам нужно выйти сразу из нескольких циклов.
Пример, в котором выйдем сразу из 2 циклов, когда произведение значений переменных-счётчиков даст число большее 10.
// обозначим внешний цикл, используя метку outer outer: for (var i = 2; i < 5; i++) { // вложенный цикл for (var j = 2; j < 5; j++) { // если условие выполняется, то прерываем работу и переходим к концу цикла с меткой outer if (i * j > 10) break outer; // выведем в консоль console.log(i + ' * ' + j + ' = ' + i * j); } } // в консоль будет выведено: 2 * 2 = 4, 2 * 3 = 6, 2 * 4 = 8, 3 * 2 = 6, 3 * 3 = 9
Кроме этого, операторы break
и continue
нельзя использовать в выражениях тернарных операторов.
Цикл for...of (новинка в ES6)
Цикл for...of
появился в стандарте ES6. Предназначен он для перебора итерируемых объектов, т.е. объектов, в которых реализован метод Symbol.iterator
. Этот метод ещё называют итератором. Именно его и использует цикл for...of
для перебора объектов.
Метод Symbol.iterator
имеется у String
, Array
, Map
, Set
, arguments
, NodeList
и других объектов.
Пример использование цикла for...of
для посимвольного перебора строки:
// переменная, содержащая строку let str = 'Новый'; // посимвольный перебор строки for (let char of str) { console.log(char); } // в консоль будет выведено: "Н", "о", "в", "ы", "й"
Пример использование цикла for...of
для перебора коллекции DOM-элементов:
let elements = document.querySelectorAll('p'); for (let element of elements) { console.log(element); }
Пример использование цикла for...of
для перебора массива:
// массив let superHeroes = ['Iron Man', 'Thor', 'Hulk']; // перебор массива for (let value of superHeroes) { console.log(value); } // в консоль будет выведено: "Iron Man", "Thor", "Hulk"
Чем цикл for...of отличается от for...in
Первое отличие цикла for...of
от for...in
заключается в том, что он может применяться только для итерируемым объектов, т.е. объектов, в которых реализован итератор (Symbol.iterator
). Цикл for...in
итератор не использует. Он предназначен для перебора любых объектов.
Второе отличие заключается в том, что цикл for...of
перебирает объект так, как это определено в итераторе. Например, в Array
итератор реализован так, что цикл for...of
пройдёт только по значениям в массиве и не будет включать в перебор другие (не индексные) свойства. Цикл for...in
организован по-другому, он перебирает все перечисляемые свойства (имена ключей) объекта, в том числе и наследуемые.
Рассмотрим эти отличия. Для этого возьмём предыдущий пример и добавим к нему пользовательское свойство, например, hero
и установим ему значение 'Wasp'
.
let superHeroes = ['Iron Man', 'Thor', 'Hulk']; superHeroes.hero = 'Wasp';
При использовании for...of
он переберёт все значения этого массива:
// цикл for...of for (let value of superHeroes) { console.log(value); } // в консоль будет выведено: "Iron Man", "Thor", "Hulk"
При использовании for...in
он переберёт все перечисляемые имена ключей этого объекта:
// цикл for...in for (let key in superHeroes) { console.log(key); } // в консоль будет выведено: 0, 1, 2, "hero"
Чтобы получить значение ключа по его имени можно воспользоваться квадратными скобками:
// цикл for...in for (let key in superHeroes) { console.log(superHeroes[key]); } // в консоль будет выведено: "Iron Man", "Thor", "Hulk", "Wasp"
Самостоятельное создание итератора для объекта
Рассмотрим ещё один пример. В этом примере мы самостоятельно определим как должен итерироваться объект. Для этого создадим объект и определим ему итератор.
Создание итератора начинается с добавления к объекту специального метода. Этот метод необходимо спроектировать так, чтобы он возвращал значения последовательно (одно за другим). Название методу согласно стандарту необходимо определить с помощью символа Symbol.iterator
. Итератор должен возвращать всего один метод next()
. Этот метод в свою очередь тоже должен возвращать объект, состоящий из 2 свойств: value
и done
. Ключ done
- булевый. Он определяет есть ли ещё значения в последовательности (false
- да, true
- нет). Ключ value
должен содержать следующее значение последовательности.
let car = { color: 'black', brand: 'Ford', // создадим итератор, используя символ [Symbol.iterator]() { // получим имена перечисляемых свойств объекта const keys = Object.keys(this); // создадим переменную (текущий индекс последовательности) let index = 0; return { next() { let done = index >= keys.length; let value = done ? undefined : keys[index++]; return { value, done } } } } } for (let key in car) { console.log(key + ' => ' + car[key]); } // в консоль будет выведено: color => "black", brand => "Ford"
Задачи по циклам
1. Написать с помощью цикла while «переворот» числа. Другими словами, нужно создать новое число, у которого цифры шли бы в обратном порядке (например: 472 -> 274).
2. Найти самую большую цифру в целом числе.
3. Вычислить сумму первой и последней цифр целого числа.
например 1 + 2 + 3 = 6
Заранее благодарю.
В консоле имена свойств выводятся не по порядку, а так: c, d, a, b.
Вопрос такой: как сделать так, чтобы они были выведены по порядку. С помощью метода .sort() у меня не получается. Спасибо!
Во-первых, «Uncaught ReferenceError: data is not defined».
Во-вторых, console.log в этом примере отсутствует. Исходя их этого, туда ничего и не выведется.
Может быть, нужно так:
Что-то вы в этом примере напутали :)
Вы работаете в цикле с arr (а должны с str). А также, неверен результат цикла в консоле. так как результатом будут не индексы букв, а сами буквы «а» и «о».
Да, есть такое :)
Спасибо! Код изменил.