loop
This commit is contained in:
parent
ab936483cd
commit
835d2ae1ce
1 changed files with 161 additions and 125 deletions
|
@ -1,24 +1,25 @@
|
|||
# Циклы while, for
|
||||
# Loops: while and for
|
||||
|
||||
При написании скриптов зачастую встает задача сделать однотипное действие много раз.
|
||||
We often have a need to perform similar actions many times in a row.
|
||||
|
||||
Например, вывести товары из списка один за другим. Или просто перебрать все числа от 1 до 10 и для каждого выполнить одинаковый код.
|
||||
For example, when we need to output goods from the list one after another. Or just write the same code for each number from 1 to 10.
|
||||
|
||||
*Loops* are a way to repeat the same part code multiple times.
|
||||
|
||||
Для многократного повторения одного участка кода - предусмотрены *циклы*.
|
||||
[cut]
|
||||
## Цикл while
|
||||
## The "while" loop
|
||||
|
||||
Цикл `while` имеет вид:
|
||||
The `while` loop has the following syntax:
|
||||
|
||||
```js
|
||||
while (условие) {
|
||||
// код, тело цикла
|
||||
while (condition) {
|
||||
// code, loop body
|
||||
}
|
||||
```
|
||||
|
||||
Пока `условие` верно -- выполняется код из тела цикла.
|
||||
While the `condition` is true -- the `code` from the loop body is executed.
|
||||
|
||||
Например, цикл ниже выводит `i` пока `i < 3`:
|
||||
For instance, the loop below outputs `i` while `i<3`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -29,44 +30,49 @@ while (i < 3) {
|
|||
}
|
||||
```
|
||||
|
||||
**Повторение цикла по-научному называется *"итерация"*. Цикл в примере выше совершает три итерации.**
|
||||
There's a special term *iteration* for each loop run. The loop in the example above makes 3 iterations.
|
||||
|
||||
Если бы `i++` в коде выше не было, то цикл выполнялся бы (в теории) вечно. На практике, браузер выведет сообщение о "зависшем" скрипте и посетитель его остановит.
|
||||
If there were no `i++` in the example above, the loop would repeat (in theory) forever. In practice, the browser would show a message about a "hanging" script and let the user stop it.
|
||||
|
||||
**Бесконечный цикл** можно сделать и проще:
|
||||
|
||||
```js
|
||||
while (true) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Условие в скобках интерпретируется как логическое значение, поэтому вместо `while (i!=0)` обычно пишут `while (i)`**:
|
||||
The `condition` is treated as a logical value, so instead of `while (i!=0)` we usually write `while (i)`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 3;
|
||||
*!*
|
||||
while (i) { // при i, равном 0, значение в скобках будет false и цикл остановится
|
||||
while (i) { // when i becomes 0, the condition is falsy and the loop stops
|
||||
*/!*
|
||||
alert( i );
|
||||
i--;
|
||||
}
|
||||
```
|
||||
|
||||
## Цикл do..while
|
||||
[smart header="Brackes are not required for a single-line body"]
|
||||
|
||||
Проверку условия можно поставить *под* телом цикла, используя специальный синтаксис `do..while`:
|
||||
If the loop body has a single statement, we can omit the brackets `{…}`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 3;
|
||||
*!*
|
||||
while (i) alert(i--);
|
||||
*/!*
|
||||
```
|
||||
[/smart]
|
||||
|
||||
## The "do..while" loop
|
||||
|
||||
The condition check can be moved *below* the loop body using the `do..while` syntax:
|
||||
|
||||
```js
|
||||
do {
|
||||
// тело цикла
|
||||
} while (условие);
|
||||
// loop body
|
||||
} while (condition);
|
||||
```
|
||||
|
||||
Цикл, описанный, таким образом, сначала выполняет тело, а затем проверяет условие.
|
||||
The loop will first execute the body and then check the condition.
|
||||
|
||||
Например:
|
||||
For example:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -77,52 +83,53 @@ do {
|
|||
} while (i < 3);
|
||||
```
|
||||
|
||||
Синтаксис `do..while` редко используется, т.к. обычный `while` нагляднее -- в нём не приходится искать глазами условие и ломать голову, почему оно проверяется именно в конце.
|
||||
This form of syntax is rarely used, because the ordinary `while` is more obvious. We don't need to eye down the code looking for the condition. Neither we need to ask ourselves a question why it is checked at the bottom.
|
||||
|
||||
## The "for" loop
|
||||
|
||||
## Цикл for
|
||||
The `for` loop is actually the most often used one.
|
||||
|
||||
Чаще всего применяется цикл `for`. Выглядит он так:
|
||||
It looks like this:
|
||||
|
||||
```js
|
||||
for (начало; условие; шаг) {
|
||||
// ... тело цикла ...
|
||||
for (begin; condition; step) {
|
||||
// ... loop body ...
|
||||
}
|
||||
```
|
||||
|
||||
Пример цикла, который выполняет `alert(i)` для `i` от `0` до `2` включительно (до `3`):
|
||||
An example of the loop which runs `alert(i)` for `i` from `0` to `3` not including `3`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
for (i = 0; i < 3; i++) { // shows 0, then 1, then 2
|
||||
alert( i );
|
||||
}
|
||||
```
|
||||
|
||||
Здесь:
|
||||
Here:
|
||||
<ul>
|
||||
<li>**Начало:** `i=0`.</li>
|
||||
<li>**Условие:** `i<3`.</li>
|
||||
<li>**Шаг:** `i++`.</li>
|
||||
<li>**Тело:** `alert(i)`, т.е. код внутри фигурных скобок (они не обязательны, если только одна операция)</li>
|
||||
<li>**Begin:** `i=0`.</li>
|
||||
<li>**Condition:** `i<3`.</li>
|
||||
<li>**Step:** `i++`.</li>
|
||||
<li>**Body:** `alert(i)`, the code inside figure brackets. Brackets not required for a single statement.</li>
|
||||
</ul>
|
||||
|
||||
Цикл выполняется так:
|
||||
The loop execution follows these steps:
|
||||
|
||||
<ol>
|
||||
<li>Начало: `i=0` выполняется один-единственный раз, при заходе в цикл.</li>
|
||||
<li>Условие: `i<3` проверяется перед каждой итерацией и при входе в цикл, если оно нарушено, то происходит выход.</li>
|
||||
<li>Тело: `alert(i)`.</li>
|
||||
<li>Шаг: `i++` выполняется после *тела* на каждой итерации, но перед проверкой условия.</li>
|
||||
<li>Идти на шаг 2.</li>
|
||||
<li>Begin: `i=0` executes only once upon entering the loop.</li>
|
||||
<li>Condition: `i<3` is checked before every iteration including the first one. If it fails, the loop stops.</li>
|
||||
<li>Body: `alert(i)` runs is the condition is truthy.</li>
|
||||
<li>Step: `i++` executes after the `body` on each iteration, but before the `condition` check.</li>
|
||||
<li>Continue to step 2.</li>
|
||||
</ol>
|
||||
|
||||
Иными словами, поток выполнения: `начало` -> (если `условие` -> `тело` -> `шаг`) -> (если `условие` -> `тело` -> `шаг`) -> ... и так далее, пока верно `условие`.
|
||||
In other words, the execution flow is: `begin` -> (if `condition` -> run `body` and `step`) -> (if `condition` -> run `body` and `step`) -> ... and so on while the `condition` is truthy.
|
||||
|
||||
[smart]
|
||||
В цикле также можно определить переменную:
|
||||
[smart header="Inline variable declaration"]
|
||||
We can declare a "counter" variable right in the beginning of the loop.
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
|
@ -130,26 +137,27 @@ for (*!*var*/!* i = 0; i < 3; i++) {
|
|||
alert(i); // 0, 1, 2
|
||||
}
|
||||
```
|
||||
|
||||
Эта переменная будет видна и за границами цикла, в частности, после окончания цикла `i` станет равно `3`.
|
||||
[/smart]
|
||||
|
||||
## Пропуск частей for
|
||||
|
||||
Любая часть `for` может быть пропущена.
|
||||
## Skipping of "for" parts
|
||||
|
||||
Например, можно убрать `начало`. Цикл в примере ниже полностью идентичен приведённому выше:
|
||||
Any part of the `for` can be skipped.
|
||||
|
||||
For example, we can remove `begin`, or move it before the actual `for`, like here:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 0;
|
||||
|
||||
for (; i < 3; i++) {
|
||||
for (; i < 3; i++) {
|
||||
alert( i ); // 0, 1, 2
|
||||
}
|
||||
```
|
||||
|
||||
Можно убрать и `шаг`:
|
||||
It would work same as `for(var i=0; ...)`.
|
||||
|
||||
We can also remove the `step` part:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -157,39 +165,41 @@ var i = 0;
|
|||
|
||||
for (; i < 3;) {
|
||||
alert( i );
|
||||
// цикл превратился в аналог while (i<3)
|
||||
// the loop became identical to while (i<3)
|
||||
}
|
||||
```
|
||||
|
||||
А можно и вообще убрать всё, получив бесконечный цикл:
|
||||
We can actually remove everything, thus creating an infinite loop:
|
||||
|
||||
```js
|
||||
for (;;) {
|
||||
// будет выполняться вечно
|
||||
// repeats without limits
|
||||
}
|
||||
```
|
||||
|
||||
При этом сами точки с запятой `;` обязательно должны присутствовать, иначе будет ошибка синтаксиса.
|
||||
Although the semicolons `;` must present, otherwise it would be a syntax error.
|
||||
|
||||
[smart header="`for..in`"]
|
||||
Существует также специальная конструкция `for..in` для перебора свойств объекта.
|
||||
There is also a special construct `for..in` to iterate over object properties.
|
||||
|
||||
Мы познакомимся с ней позже, когда будем [говорить об объектах](#for..in).
|
||||
We'll get to it later while [talking about objects](#for..in).
|
||||
[/smart]
|
||||
|
||||
|
||||
## Прерывание цикла: break
|
||||
## Breaking the loop
|
||||
|
||||
Выйти из цикла можно не только при проверке условия но и, вообще, в любой момент. Эту возможность обеспечивает директива `break`.
|
||||
Normally the loop exists when the condition becomes falsy.
|
||||
|
||||
Например, следующий код подсчитывает сумму вводимых чисел до тех пор, пока посетитель их вводит, а затем -- выдаёт:
|
||||
But we can force the exit any moment. There's a special `break` directive for that.
|
||||
|
||||
For example, this code calculates the sum of numbers until the user keeps entering them. And then outputs it:
|
||||
|
||||
```js
|
||||
var sum = 0;
|
||||
|
||||
while (true) {
|
||||
|
||||
var value = +prompt("Введите число", '');
|
||||
var value = +prompt("Enter a number", '');
|
||||
|
||||
*!*
|
||||
if (!value) break; // (*)
|
||||
|
@ -198,22 +208,20 @@ while (true) {
|
|||
sum += value;
|
||||
|
||||
}
|
||||
alert( 'Сумма: ' + sum );
|
||||
alert( 'Sum: ' + sum );
|
||||
```
|
||||
|
||||
Директива `break` в строке `(*)`, если посетитель ничего не ввёл, полностью прекращает выполнение цикла и передаёт управление на строку за его телом, то есть на `alert`.
|
||||
The `break` directive is activated in the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing the control to the first line after it's loop. Namely, `alert`.
|
||||
|
||||
Вообще, сочетание "бесконечный цикл + break" -- отличная штука для тех ситуаций, когда условие, по которому нужно прерваться, находится не в начале-конце цикла, а посередине.
|
||||
Actually, the composition: "an infinite loop + break" is a great thing for situations when the condition must be checked not in beginning/end of the loop, but in the middle.
|
||||
|
||||
## Следующая итерация: continue [#continue]
|
||||
## Continue to the next iteration [#continue]
|
||||
|
||||
Директива `continue` прекращает выполнение *текущей итерации* цикла.
|
||||
The `continue` is a younger sister of `break`. It doesn't break the whole loop, just the current body execution, as if it finished.
|
||||
|
||||
Она -- в некотором роде "младшая сестра" директивы `break`: прерывает не весь цикл, а только текущее выполнение его тела, как будто оно закончилось.
|
||||
We can use it if we're done on the current iteration and would like to move to the next one.
|
||||
|
||||
Её используют, если понятно, что на текущем повторе цикла делать больше нечего.
|
||||
|
||||
Например, цикл ниже использует `continue`, чтобы не выводить чётные значения:
|
||||
The loop above uses `continue` to output only odd values:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
|
@ -225,11 +233,10 @@ for (var i = 0; i < 10; i++) {
|
|||
}
|
||||
```
|
||||
|
||||
Для чётных `i` срабатывает `continue`, выполнение тела прекращается и управление передаётся на следующий проход `for`.
|
||||
For even `i` the `continue` directive stops body execution, passing the control to the next iteration of `for` (with the next number).
|
||||
|
||||
[smart header="Директива `continue` позволяет обойтись без скобок"]
|
||||
|
||||
Цикл, который обрабатывает только нечётные значения, мог бы выглядеть так:
|
||||
[smart header="`continue` allows to decrease nesting level"]
|
||||
A loop for odd-only values could look like this:
|
||||
|
||||
```js
|
||||
for (var i = 0; i < 10; i++) {
|
||||
|
@ -241,51 +248,80 @@ for (var i = 0; i < 10; i++) {
|
|||
}
|
||||
```
|
||||
|
||||
С технической точки зрения он полностью идентичен. Действительно, вместо `continue` можно просто завернуть действия в блок `if`. Однако, мы получили дополнительный уровень вложенности фигурных скобок. Если код внутри `if` более длинный, то это ухудшает читаемость, в отличие от варианта с `continue`.
|
||||
From the technical point of view it's identical. Really, we can just wrap the code in the `if` block instead of `continue`.
|
||||
|
||||
But we got one more figure brackets nesting level. If the code inside `if` is longer than a few lines, that lowers the overall readability.
|
||||
[/smart]
|
||||
|
||||
[warn header="Нельзя использовать break/continue справа от оператора '?'"]
|
||||
Обычно мы можем заменить `if` на оператор вопросительный знак `'?'`.
|
||||
[warn header="No `break/continue` to the right side of '?'"]
|
||||
Usually it is possible to replace `if` with a question mark operator `'?'`.
|
||||
|
||||
То есть, запись:
|
||||
The code:
|
||||
|
||||
```js
|
||||
if (условие) {
|
||||
if (condition) {
|
||||
a();
|
||||
} else {
|
||||
b();
|
||||
}
|
||||
```
|
||||
|
||||
...Аналогична записи:
|
||||
...Is identical to:
|
||||
|
||||
```js
|
||||
условие ? a() : b();
|
||||
condition ? a() : b();
|
||||
```
|
||||
|
||||
В обоих случаях в зависимости от условия выполняется либо `a()` либо `b()`.
|
||||
In both cases either `a()` or `b()` is executed depending on the `condition`.
|
||||
|
||||
Но разница состоит в том, что оператор вопросительный знак `'?'`, использованный во второй записи, возвращает значение.
|
||||
Please note that syntax constructs that do not return a value cannot be used in `'?'`. Directives `break/continue` are not expressions. They are disallowed here.
|
||||
|
||||
**Синтаксические конструкции, которые не возвращают значений, нельзя использовать в операторе `'?'`.**
|
||||
|
||||
К таким относятся большинство конструкций и, в частности, `break/continue`.
|
||||
|
||||
Поэтому такой код приведёт к ошибке:
|
||||
The code below will give a syntax error:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
(i > 5) ? alert(i) : *!*continue*/!*;
|
||||
```
|
||||
|
||||
Впрочем, как уже говорилось ранее, оператор вопросительный знак `'?'` не стоит использовать таким образом. Это -- всего лишь ещё одна причина, почему для проверки условия предпочтителен `if`.
|
||||
That's one more reason not to use a question mark operator `'?'` instead of `if`.
|
||||
[/warn]
|
||||
|
||||
## Метки для break/continue
|
||||
## Labels for break/continue
|
||||
|
||||
Бывает нужно выйти одновременно из нескольких уровней цикла.
|
||||
Sometimes we need to break out from multiple nested loops at once.
|
||||
|
||||
Например, внутри цикла по `i` находится цикл по `j`, и при выполнении некоторого условия мы бы хотели выйти из обоих циклов сразу:
|
||||
For example, in the code below we loop over `i` and `j` asking for values on coordinates `(i, j)` from `(0,0)` to `(3,3)`:
|
||||
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
for (var i = 0; i < 3; i++) {
|
||||
|
||||
for (var j = 0; j < 3; j++) {
|
||||
|
||||
var input = prompt(`Value at coords (${i},${j})`, '');
|
||||
// do something with the value...
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
alert('Done!');
|
||||
```
|
||||
|
||||
Let's say we need a way to stop the process. Like if we user decides to cancel the input.
|
||||
|
||||
The ordinary `break` after `input` would only break the iterations over `j`. That's not sufficient.
|
||||
|
||||
A *label* is an identifier with a colon before a loop:
|
||||
```js
|
||||
labelName: for(...) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
We can put the `labelName` after a break statement, and it will break out of the labelled loop.
|
||||
|
||||
Like here:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
|
@ -293,27 +329,22 @@ if (условие) {
|
|||
|
||||
for (var j = 0; j < 3; j++) {
|
||||
|
||||
var input = prompt('Значение в координатах '+i+','+j, '');
|
||||
var input = prompt(`Value at coords (${i},${j})`, '');
|
||||
|
||||
// если отмена ввода или пустая строка -
|
||||
// завершить оба цикла
|
||||
// if an empty string or canceled, then break out of both loops
|
||||
if (!input) *!*break outer*/!*; // (*)
|
||||
|
||||
// do something with the value...
|
||||
}
|
||||
}
|
||||
alert('Готово!');
|
||||
alert('Done!');
|
||||
```
|
||||
|
||||
В коде выше для этого использована *метка*.
|
||||
In the code above `break outer` looks upwards for the label named `outer` and breaks out of that loop.
|
||||
|
||||
Метка имеет вид `"имя:"`, имя должно быть уникальным. Она ставится перед циклом, вот так:
|
||||
So the control goes straight from `(*)` to `alert('Done!')`.
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
outer: for (var i = 0; i < 3; i++) { ... }
|
||||
```
|
||||
|
||||
Можно также выносить её на отдельную строку:
|
||||
We can also move a label into the separate string:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
|
@ -321,29 +352,34 @@ outer:
|
|||
for (var i = 0; i < 3; i++) { ... }
|
||||
```
|
||||
|
||||
Вызов `break outer` ищет ближайший внешний цикл с такой меткой и переходит в его конец.
|
||||
The `continue` directive can also be used with a label. In this case the execution would jump onto the next iteration of the labelled loop.
|
||||
|
||||
В примере выше это означает, что будет разорван самый внешний цикл и управление перейдёт на `alert`.
|
||||
[warn header="Labels are not Goto"]
|
||||
Labels do not allow to jump into an arbitrary place of code.
|
||||
|
||||
Директива `continue` также может быть использована с меткой, в этом случае управление перепрыгнет на следующую итерацию цикла с меткой.
|
||||
For example, it is impossible to do like this:
|
||||
```js
|
||||
break label; // jumps to label? No.
|
||||
|
||||
## Итого
|
||||
label: for(...)
|
||||
```
|
||||
|
||||
The call to a `break` is only possible from inside the loop, and the label must be somewhere upwards from the `break`.
|
||||
[/warn]
|
||||
|
||||
## Summary
|
||||
|
||||
There are 3 types of loops in JavaScript:
|
||||
|
||||
JavaScript поддерживает три вида циклов:
|
||||
<ul>
|
||||
<li>`while` -- проверка условия перед каждым выполнением.</li>
|
||||
<li>`do..while` -- проверка условия после каждого выполнения.</li>
|
||||
<li>`for` -- проверка условия перед каждым выполнением, а также дополнительные настройки.</li>
|
||||
<li>`while` -- the condition is checked before each iteration.</li>
|
||||
<li>`do..while` -- the condition is checked after each iteration.</li>
|
||||
<li>`for` -- the condition is checked before each iteration, additional settings available.</li>
|
||||
</ul>
|
||||
|
||||
Чтобы организовать бесконечный цикл, используют конструкцию `while(true)`. При этом он, как и любой другой цикл, может быть прерван директивой `break`.
|
||||
|
||||
Если на данной итерации цикла делать больше ничего не надо, но полностью прекращать цикл не следует -- используют директиву `continue`.
|
||||
|
||||
Обе этих директивы поддерживают "метки", которые ставятся перед циклом. Метки -- единственный способ для `break/continue` повлиять на выполнение внешнего цикла.
|
||||
|
||||
Заметим, что метки не позволяют прыгнуть в произвольное место кода, в JavaScript нет такой возможности.
|
||||
|
||||
To make in "infinite" loop, usually the `while(true)` construct is used. Such a loop, just like any other, can be stopped with the `break` directive.
|
||||
|
||||
If we don't want to do anything more on this iteration and would like to forward on to the next one -- use the `continue` directive.
|
||||
|
||||
Both these directives support labels before the loop. A label is the only way for `break/continue` to go up the nesting and affect the outer loop.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue