Перевод не завершен. Пожалуйста, помогите перевести эту статью с английского.

Языки программирования очень полезны для быстрой реализации повторяющихся задач. От базовых числовых операций до любой другой ситуации, когда у вас есть много похожих операций, которые нужно выполнить. В этой статье мы рассмотрим структуры циклов, доступные в JavaScript, которые можно использовать для этих целей.

Требования: Базовые значения компьютерной системы и базовое понимаение HTML и CSS, JavaScript первые шаги.
Цель: Понять как работают циклы в JavaScript.

Повторяй  меня

Циклы, циклы, циклы. Помимо того, что они связаны с производством злаковых, смене дня и ночи и музыкальным производством, они также являются важной концепцией в программировании. Использование циклов связано с повторением одного и того-же действия, в надежде на улучшение, которая называется итерацией в программировании.

Давайте рассмотрим случай фермереа, который уверен, что у него достаточно еды, чтобы кормить всю свою семью в течении недели. Для этого можно использовать цикл:


Цикл обычно выполняет одну из следующих функций:

  •  Счетчик, который инициализируется с определенного значения - это и есть начальная точка цикла ("Начало: у меня нет еды (i have no food)", выше)
  • Условие выхода, которое является критерием, при котором цикл останавливается - обычно цикл достигает определенного значения. Это иллюстрируется словами "Достаточно ли у меня еды? (Have i got enough food?)", выше. Предположим ему нужно 10 порций еды, чтобы прокормить семью.
  • Итератор, который обычно увеличивает счетчик на некоторе значение на каждом шаге цикла, пока не достигнуто условие выхода. Мы явно не иллюстрировали это выше, но можно предположить что фермер собирает 2 порции еды в час. После каждого часа, количество еды, которое у него имеется увеличивается на 2, и он проверяет достаточно ли у него еды сейчас. Если у него собралось 10 порций (условие выхода), он может остановить сбор и вернуться домой.

В псевдокоде (pseudocode) , это будет выглядеть следующим образом:

loop(food = 0; foodNeeded = 10) {
  if (food = foodNeeded) {
    exit loop;
    // У нас достаточно еды, пора домой
  } else {
    food += 2; // Прошел час, количество еды увеличилось на 2
    // переход на следующую итерацию цикла.
  }
}

Таким образом, необходимое количество еды устанавливается равным 10, а изначально фермер не имеет ни одной порции, т.е. начало равно 0. На каждой итерации цикла проверяем, соответствует ли собранное количество еды, с тем количеством, которое ему необходимо. Если это так, мы можем выйти из цикла, иначе же фермер собирает еще 2 порции и снова переходит к проверке.

Зачем беспокоиться?

На этом этапе вы, вероятно, понимаете какая сила стоит за циклами, но думаете: "Хорошо, но как это мне поможет писать код на JavaScript". Как мы говорили ранее, циклы - это постоянно повторять одно и тоже действие, что отлично подойдет для быстрого выполнения повторяющихся задач.

 

Часто код будет немного отличаться на каждой последующей итерации цикла, а это означает, что вы можете выполнять задачи, которые похожи, но у них есть некоторые различия. Если вы хотите выполнять разные задачи, а не одну и ту же каждый раз.

Давайте рассмотрим пример, ккоторый проиллюстрирует вам, почему циклы - это полезная вещь. Предположим мы хотели нарисовать 100 случайных кругов на элементе <canvas>. Нажмите кнопку "Обновить", чтобы снова и снова запускать пример и увидеть, что круги рисуются случайным образом.

Вам необязательно понимать все части кода, но давайте посмотрим на место, где рисуются 100 кругов.

for (var i = 0; i < 100; i++) {
  ctx.beginPath();
  ctx.fillStyle = 'rgba(255,0,0,0.5)';
  ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
  ctx.fill();
}
  • random(), описана выше в коде, возвращает случайное число между 0 и x-1.
  • WIDTH и HEIGHT - это высота и ширина  окна браузера.

Вы должны понять основную идею - мы используем цикл для запуска 100 итераций этого кода, каждая из которых рисует круг в случайном месте в окне. Количество кода было бы одинаковым, если бы нам нужно было нарисовать 10, 100 или 1000 кругов, поменяется лишь одно число.

Если бы мы не использовали циклы, нам бы пришлось повторить следующий код, для отрисовки каждого круга:

ctx.beginPath();
ctx.fillStyle = 'rgba(255,0,0,0.5)';
ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
ctx.fill();

Это было бы скучно и очень усложняло бы подержку кода в будущем, т.к. если бы вам захотелось что-то изменить в каждойитерации цикла, пришлось бы изменять все части кода по отдельности. А также это усложнаяет поиск ошибки, т.к. если бы вы совершили логическую ошибку при описании одной из итераций, можно потратить много времени на ее поиски.

The standard for loop

Давайте разберем другие виды конструктора циклов. Первый, который вы будете использовать чаще всего  - это цикл for  — он имеет следующий синтаксис:

for (initializer; exit-condition; final-expression) {
  // код для выполнения
}

Тут имеем:

  1. Ключевое слово for, за которым следуют круглые скобки.
  2. В круглых скобках у нас есть три части, разделенные точой с запятой:
    1. Инициализатор — обычно это переменная численного типа, которая увеличивается каждую итерацию, чтобы посчитать количество шагов цикла. Ее также называет счетчиком.
    2. Условие выхода - как упоминалос ранее, определяет, когда цикл должен остановиться. Обычно это выражение с оператором сравнения. Проверка, выполнено ли условие выхода.
    3. Окончательное выражение - какое-либо действие, которое происходит, когда цикл проходит полную итерацию. Обычно он служит для увеличения (или уменьшения) переменной счетчика, чтобы приблизить ее значение к условию выхода.
  3. Фигурные скобки, содержащие блок кода. Этот код будет запускаться на каждой итерации цикла.

Давайте посмотрим на пример, чтобы разобраться в этом более детально.

var cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
var info = 'Моих кошек зовут ';
var para = document.querySelector('p');

for (var i = 0; i < cats.length; i++) {
  info += cats[i] + ', ';
}

para.textContent = info;

Этот блок кода будет иметь следующий результат:

Заметка: Вы можете найти этот  пример на GitHub или  посмотреть онлайн.

Это показывает, что цикл используется для перебора элементов массива и делает что-то с каждлым из них - это очень распространенный шаблон в JavaScript. Подробнее:

  1. Итератор, i, начинает с 0 (var i = 0).
  2. Цикл запускается, пока значение итератора не будет больше длины массива кошек. Это важно - условие выхода показывает когда именно цикл должен работать, а когда нужно выйти из цикла. Поэтому в случае, пока i < cats.lenght по-врежнему возвращает true, цикл будет работать.
  3. Внутри тела цикла мы соединяем текущий элемент цикла (cats[i] is cats[где i находится в данный момент]) с запятой и пробелом. Итак:
    1. В начале, i = 0, поэтому cats[0] + ', ' соеденятся в ("Bill, ").
    2. На втором шаге, i = 1, поэтому cats[1] + ', ' соединятся в ("Jeff, ")
    3. И так далее. В конце каждого цикла i увеличится на 1 (i++) , и процесс будет начинаться заново.
  4. Когда i достигнет величины cats.length цикл остановится и браузер перейдет на код следйющий после цикла.

Заметка: Мы добавили условия выхода i < cats.length, а не i <= cats.length, потому что компьютеры считают с  0,  а не 1 — в начале i = 0 и увеличивается до i = 4 (индекс последнего элемента массива). cats.length возвращает 5, т.к. в массиве 5 элементов, но нам не нужно увеличивать до i = 5, т.к. cats[5] вернет undefined (в массиве нет элемента с индексом 5). Поэтому cats.length (i <), не одно и тоже что cats.length (i <=).

Заметка: Стандартной ошибкой с условием выхода является использование условия "равный" (===) ,а не "меньше или равно" (<=). Если нам нужно увеличить счетчик до i = 5, условие выхода должно быть i <= cats.length. Если мы установим i === cats.length, цикл не начнется, т.к. i не равно 5 на самой первой итерации, поэтому цикл остановится сразу.

Одна небольшая проблема, с которой мы сталкнемся состоит в том, что выходная строка будет выглядеть следующим образом:

Моих кошек зовут Bill, Jeff, Pete, Biggles, Jasmin,

В идеале мы хотим изменить соединение строк на последней итерации цикла, чтобы в конце предложения мы не получили запятую. Это не проблема, мы может добавить условное выражение внутри цикла for для обработки этого особого случая:

for (var i = 0; i < cats.length; i++) {
  if (i === cats.length - 1) {
    info += 'и ' + cats[i] + '.';
  } else {
    info += cats[i] + ', ';
  }
}

Заметка: Вы можете найти этот пример на GitHub или посмотреть онлайн.

Важно: С цкилом for, также как и сдругими циклами, вы должны убедиться что инициализатор (счетчик) и окончательное выражение построены так, что они достигнут условия выхода. Если этого не произойдет, то цикл будет продолжаться вечно. В коченом счете браузер заставит его остановится или выдаст ошибку . Это назвается бесконечным циклом.

Выход из цикла с помощью break

Если вы хотите выйти из цикла перед завершением всех иттераций, вы можете использовать условие break . Мы уже встречались с ним в предыдущей статье, когда рассматривали switch условия — когда case находит switch условие и оно совпадает с вводными данными, условие break моментально выходит из конструкции switch и запускает следующий после нее код.

Тоже самое и с циклами — условие break моментально закончит цикл и заставит браузер запустить следующий после цикла код.

Предположим, в массиве данных мы хотим найти имена контактов и телефонные номера, а вернуть только лишь номер, который мы нашли.
Для начала, простой HTML — текст <input> позволяющий нам ввести имя для поиска, элемент (кнопка) <button> для подтверждения поиска, и элемент <p> для отображения результата:

<label for="search">Поиск по имени: </label>
<input id="search" type="text">
<button>Поиск</button>

<p></p>

Теперь в JavaScript:

var contacts = ['Chris:2232322', 'Sarah:3453456', 'Bill:7654322', 'Mary:9998769', 'Dianne:9384975'];
var para = document.querySelector('p');
var input = document.querySelector('input');
var btn = document.querySelector('button');

btn.addEventListener('click', function() {
  var searchName = input.value;
  input.value = '';
  input.focus();
  for (var i = 0; i < contacts.length; i++) {
    var splitContact = contacts[i].split(':');
    if (splitContact[0] === searchName) {
      para.textContent = splitContact[0] + '\'s number is ' + splitContact[1] + '.';
      break;
    } else {
      para.textContent = 'Контакт не найден.';
    }
  }
});

  1. Для начала у нас определены некоторые переменные — у нас есть массив с контактной информацией, каждый элемент которого это строка, содержащая в себе имя и номер телефона, которые разделенны двоеточием.
  2. Далее, мы применяем обработчик события для кнопки (btn), так, чтобы, когда кнопка нажата, был запущен некоторый код для того, чтобы осуществиить поиск и отобразить поисковые результаты.
  3. Мы размещаем введенные текстовые данные в переменную  searchName, перед тем как отчистить введеный текст и сфокусировать снова, для нового поиска.
  4. Теперь перейдем к интересной части цикла for:
    1. Мы начали счетчик с 0, запускать цикл до тех пор, пока счетчик не станет меньше, чем contacts.length, и инкремент i увеличивающийся на 1 после каждой иттерации цикла.
    2. Внутри цикла мы сначала разбиваем/разделяем текущий контакт (contacts[i]) на символе двоеточия, и сохраняем полученные два значения в массиве с  названием splitContact.
    3. Затем мы используем условный оператор, чтобы проверить, равно ли splitContact[0] (имя контакта) введеному searchName. Если это так, мы выводим строку в абзац, чтобы сообщить, каков номер контакта, и используем break для завершения цикла.
  5. После иттерации (contacts.length-1), если имя контакта не совпадает с введенным именем в поисковом запросе, для текста абзаца устанавливается: «Контакт не найден», и цикл продолжает повторяться.

Заметка: Вы можете посмотреть исходный код на гитхабе (full source code on GitHub) или запустить его (also see it running live).

Skipping iterations with continue

The continue statement works in a similar manner to break, but instead of breaking out of the loop entirely, it skips to the next iteration of the loop. Let's look at another example that takes a number as an input, and returns only the numbers that are squares of integers (whole numbers).

The HTML is basically the same as the last example — a simple text input, and a paragraph for output. The JavaScript is mostly the same too, although the loop itself is a bit different:

var num = input.value;

for (var i = 1; i <= num; i++) {
  var sqRoot = Math.sqrt(i);
  if (Math.floor(sqRoot) !== sqRoot) {
    continue;
  }

  para.textContent += i + ' ';
}

Here's the output:

  1. In this case, the input should be a number (num). The for loop is given a counter starting at 1 (as we are not interested in 0 in this case), an exit condition that says the loop will stop when the counter becomes bigger than the input num, and an iterator that adds 1 to the counter each time.
  2. Inside the loop, we find the square root of each number using Math.sqrt(i), then check whether the square root is an integer by testing whether it is the same as itself when it has been rounded down to the nearest integer (this is what Math.floor() does to the number it is passed).
  3. If the square root and the rounded down square root do not equal one another (!==), it means that the square root is not an integer, so we are not interested in it. In such a case, we use the continue statement to skip on to the next loop iteration without recording the number anywhere.
  4. If the square root IS an integer, we skip past the if block entirely so the continue statement is not executed; instead, we concatenate the current i value plus a space on to the end of the paragraph content.

Note: You can view the full source code on GitHub too (also see it running live).

while and do ... while

for is not the only type of loop available in JavaScript. There are actually many others and, while you don't need to understand all of these now, it is worth having a look at the structure of a couple of others so that you can recognize the same features at work in a slightly different way.

First, let's have a look at the while loop. This loop's syntax looks like so:

initializer
while (exit-condition) {
  // code to run

  final-expression
}

This works in a very similar way to the for loop, except that the initializer variable is set before the loop, and the final-expression is included inside the loop after the code to run — rather than these two items being included inside the parentheses. The exit-condition is included inside the parentheses, which are preceded by the while keyword rather than for.

The same three items are still present, and they are still defined in the same order as they are in the for loop — this makes sense, as you still have to have an initializer defined before you can check whether it has reached the exit-condition; the final-condition is then run after the code inside the loop has run (an iteration has been completed), which will only happen if the exit-condition has still not been reached.

Let's have a look again at our cats list example, but rewritten to use a while loop:

var i = 0;

while (i < cats.length) {
  if (i === cats.length - 1) {
    info += 'and ' + cats[i] + '.';
  } else {
    info += cats[i] + ', ';
  }

  i++;
}

Note: This still works just the same as expected — have a look at it running live on GitHub (also view the full source code).

The do...while loop is very similar, but provides a variation on the while structure:

initializer
do {
  // code to run

  final-expression
} while (exit-condition)

In this case, the initializer again comes first, before the loop starts. The do keyword directly precedes the curly braces containing the code to run and the final-expression.

The differentiator here is that the exit-condition comes after everything else, wrapped in parentheses and preceded by a while keyword. In a do...while loop, the code inside the curly braces is always run once before the check is made to see if it should be executed again (in while and for, the check comes first, so the code might never be executed).

Let's rewrite our cat listing example again to use a do...while loop:

var i = 0;

do {
  if (i === cats.length - 1) {
    info += 'and ' + cats[i] + '.';
  } else {
    info += cats[i] + ', ';
  }

  i++;
} while (i < cats.length);

Note: Again, this works just the same as expected — have a look at it running live on GitHub (also view the full source code).

Important: With while and do...while — as with all loops — you must make sure that the initializer is iterated so that it eventually reaches the exit condition. If not, the loop will go on forever, and either the browser will force it to stop, or it will crash. This is called an infinite loop.

Active learning: Launch countdown!

In this exercise, we want you to print out a simple launch countdown to the output box, from 10 down to Blast off. Specifically, we want you to:

  • Loop from 10 down to 0. We've provided you with an initializer — var i = 10;.
  • For each iteration, create a new paragraph and append it to the output <div>, which we've selected using var output = document.querySelector('.output');. In comments, we've provided you with three code lines that need to be used somewhere inside the loop:
    • var para = document.createElement('p'); — creates a new paragraph.
    • output.appendChild(para); — appends the paragraph to the output <div>.
    • para.textContent = — makes the text inside the paragraph equal to whatever you put on the right hand side, after the equals sign.
  • Different iteration numbers require different text to be put in the paragraph for that iteration (you'll need a conditional statement and multiple para.textContent = lines):
    • If the number is 10, print "Countdown 10" to the paragraph.
    • If the number is 0, print "Blast off!" to the paragraph.
    • For any other number, print just the number to the paragraph.
  • Remember to include an iterator! However, in this example we are counting down after each iteration, not up, so you don't want i++ — how do you iterate downwards?

If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution.

Active learning: Filling in a guest list

In this exercise, we want you to take a list of names stored in an array, and put them into a guest list. But it's not quite that easy — we don't want to let Phil and Lola in because they are greedy and rude, and always eat all the food! We have two lists, one for guests to admit, and one for guests to refuse.

Specifically, we want you to:

  • Write a loop that will iterate from 0 to the length of the people array. You'll need to start with an initializer of  var i = 0;, but what exit condition do you need?
  • During each loop iteration, check if the current array item is equal to "Phil" or "Lola" using a conditional statement:
    • If it is, concatenate the array item to the end of the refused paragraph's textContent, followed by a comma and a space.
    • If it isn't, concatenate the array item to the end of the admitted paragraph's textContent, followed by a comma and a space.

We've already provided you with:

  • var i = 0; — Your initializer.
  • refused.textContent += — the beginnings of a line that will concatenate something on to the end of refused.textContent.
  • admitted.textContent += — the beginnings of a line that will concatenate something on to the end of admitted.textContent.

Extra bonus question — after completing the above tasks successfully, you will be left with two lists of names, separated by commas, but they will be untidy — there will be a comma at the end of each one. Can you work out how to write lines that slice the last comma off in each case, and add a full stop to the end? Have a look at the Useful string methods article for help.

If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution.

Which loop type should you use?

For basic uses, for, while, and do...while loops are largely interchangeable. They can all be used to solve the same problems, and which one you use will largely depend on your personal preference — which one you find easiest to remember or most intuitive. Let's have a look at them again.

First for:

for (initializer; exit-condition; final-expression) {
  // code to run
}

while:

initializer
while (exit-condition) {
  // code to run

  final-expression
}

and finally do...while:

initializer
do {
  // code to run

  final-expression
} while (exit-condition)

We would recommend for, at least to begin with, as it is probably the easiest for remembering everything — the initializer, exit-condition, and final-expression all have to go neatly into the parentheses, so it is easy to see where they are and check that you aren't missing them.

Note: There are other loop types/features too, which are useful in advanced/specialized situations and beyond the scope of this article. If you want to go further with your loop learning, read our advanced Loops and iteration guide.

Conclusion

This article has revealed to you the basic concepts behind, and different options available when, looping code in JavaScript. You should now be clear on why loops are a good mechanism for dealing with repetitive code, and be raring to use them in your own examples!

If there is anything you didn't understand, feel free to read through the article again, or contact us to ask for help.

See also

 

In this module

 

Метки документа и участники

Внесли вклад в эту страницу: mdnwebdocs-bot, ohnoitsdasha, PavelDikiy, satori, Novo
Обновлялась последний раз: mdnwebdocs-bot,