renovations
This commit is contained in:
parent
87a3588057
commit
223dd884ae
100 changed files with 638 additions and 658 deletions
|
@ -92,9 +92,9 @@ alert(isAdmin);
|
|||
|
||||
## Особенности встроенных функций
|
||||
|
||||
Место, где выводится модальное окно с вопросом, и внешний вид окна выбирает браузер. Разработчик не может на это влиять.
|
||||
Конкретное место, где выводится модальное окно с вопросом -- обычно это центр браузера, и внешний вид окна выбирает браузер. Разработчик не может на это влиять.
|
||||
|
||||
С одной стороны -- это недостаток, т.к. нельзя вывести окно в своем дизайне.
|
||||
С одной стороны -- это недостаток, так как нельзя вывести окно в своем, особо красивом, дизайне.
|
||||
|
||||
С другой стороны, преимущество этих функций по сравнению с другими, более сложными методами взаимодействия, которые мы изучим в дальнейшем -- как раз в том, что они очень просты.
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
@ -24,14 +24,20 @@ if (year != 2011) {
|
|||
}
|
||||
```
|
||||
|
||||
**Рекомендуется использовать фигурные скобки всегда, даже когда команда одна.** Это улучшает читаемость кода.
|
||||
**Рекомендуется использовать фигурные скобки всегда, даже когда команда одна.**
|
||||
|
||||
Это улучшает читаемость кода.
|
||||
|
||||
|
||||
## Преобразование к логическому типу
|
||||
|
||||
Оператор `if (...)` вычисляет и преобразует выражение в скобках к логическому типу.
|
||||
|
||||
**В логическом контексте число `0`, пустая строка `""`, `null` и `undefined`, а также `NaN` являются `false`, остальные значения -- `true`.**
|
||||
В логическом контексте:
|
||||
<ul>
|
||||
<li>Число `0`, пустая строка `""`, `null` и `undefined`, а также `NaN` являются `false`,</li>
|
||||
<li>Остальные значения -- `true`.</li>
|
||||
</ul>
|
||||
|
||||
Например, такое условие никогда не выполнится:
|
||||
|
||||
|
@ -41,7 +47,7 @@ if (0) { // 0 преобразуется к false
|
|||
}
|
||||
```
|
||||
|
||||
... А такое -- выполнится всегда:
|
||||
...А такое -- выполнится всегда:
|
||||
|
||||
```js
|
||||
if (1) { // 1 преобразуется к true
|
||||
|
@ -49,7 +55,7 @@ if (1) { // 1 преобразуется к true
|
|||
}
|
||||
```
|
||||
|
||||
Вычисление условия в проверке `if (year != 2011)` может быть вынесено в отдельную переменную:
|
||||
Можно и просто передать уже готовое логическое значение, к примеру, заранее вычисленное в переменной:
|
||||
|
||||
```js
|
||||
var cond = (year != 2011); // true/false
|
||||
|
@ -65,7 +71,7 @@ if (cond) {
|
|||
|
||||
```js
|
||||
//+ run
|
||||
var year = prompt('Введите год ECMA-262 5.1', '');
|
||||
var year = prompt('Введите год появления стандарта ECMA-262 5.1', '');
|
||||
|
||||
if (year == 2011) {
|
||||
alert('Да вы знаток!');
|
||||
|
@ -121,7 +127,7 @@ alert(access);
|
|||
условие ? значение1 : значение2
|
||||
```
|
||||
|
||||
Проверяется условие, затем если оно верно -- возвращается `значение1 `, если неверно -- `значение2`, например:
|
||||
Проверяется условие, затем если оно верно -- возвращается `значение1`, если неверно -- `значение2`, например:
|
||||
|
||||
```js
|
||||
access = (age > 14) ? true : false;
|
||||
|
@ -141,7 +147,6 @@ access = age > 14 ? true : false;
|
|||
```js
|
||||
access = age > 14;
|
||||
```
|
||||
|
||||
[/smart]
|
||||
|
||||
[smart header="\"Тернарный оператор\""]
|
||||
|
@ -152,39 +157,36 @@ access = age > 14;
|
|||
|
||||
## Несколько операторов '?'
|
||||
|
||||
Несколько операторов `if..else` можно заменить последовательностью операторов `'?'`. Например:
|
||||
Последовательность операторов `'?'` позволяет вернуть значение в зависимости не от одного условия, а от нескольких.
|
||||
|
||||
Например:
|
||||
```js
|
||||
//+ run
|
||||
var a = prompt('a?', 1);
|
||||
var age = prompt('возраст?', 18);
|
||||
|
||||
*!*
|
||||
var res = (a == 1) ? 'значение1' :
|
||||
(a == 2) ? 'значение2' :
|
||||
(a > 2) ? 'значение3' :
|
||||
'значение4';
|
||||
*/!*
|
||||
var message = (age < 3) ? 'Здравствуй, малыш!' :
|
||||
(age < 18) ? 'Привет!' :
|
||||
(age < 100) ? 'Здравствуйте!' :
|
||||
'Какой необычный возраст!';
|
||||
|
||||
alert(res);
|
||||
alert(message);
|
||||
```
|
||||
|
||||
Поначалу может быть сложно понять, что происходит. Однако, внимательно приглядевшись, мы замечаем, что это *обычный `if..else`*!
|
||||
Поначалу может быть сложно понять, что происходит. Однако, внимательно приглядевшись, мы замечаем, что это обычная последовательная проверка!
|
||||
|
||||
Вопросительный знак проверяет сначала `a == 1`, если верно -- возвращает `значение1`, если нет -- идет проверять `a == 2`. Если это верно -- возвращает `значение2`, иначе проверка `a > 2` и `значение3`... Наконец, если ничего не верно, то `значение4`.
|
||||
Вопросительный знак проверяет сначала `age < 3`, если верно -- возвращает `'Здравствуй, малыш!'`, если нет -- идет за двоеточие и проверяет `age < 18`. Если это верно -- возвращает `'Привет!'`, иначе проверка `age < 100` и `'Здравствуйте!'`... И наконец, если ничего из этого не верно, то `'Какой необычный возраст!'`.
|
||||
|
||||
Альтернативный вариант с `if..else`:
|
||||
То же самое через `if..else`:
|
||||
|
||||
```js
|
||||
var res;
|
||||
|
||||
if (a == 1) {
|
||||
res = 'значение1';
|
||||
} else if (a == 2) {
|
||||
res = 'значение2';
|
||||
} else if (a > 2) {
|
||||
res = 'значение3';
|
||||
if (age < 3) {
|
||||
message = 'Здравствуй, малыш!';
|
||||
} else if (a < 18) {
|
||||
message = 'Привет!';
|
||||
} else if (age < 100) {
|
||||
message = 'Здравствуйте!';
|
||||
} else {
|
||||
res = 'значение4';
|
||||
message = 'Какой необычный возраст!';
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -225,3 +227,7 @@ if (company == 'Netscape') {
|
|||
*/!*
|
||||
```
|
||||
|
||||
При чтении кода глаз идёт вертикально и конструкции, занимающие несколько строк, с понятной вложенностью, воспринимаются гораздо легче. Возможно, вы и сами почувствуете, пробежавшись глазами, что синтаксис с `if` более прост и очевиден чем с оператором `'?'`.
|
||||
|
||||
Смысл оператора `'?'` -- вернуть то или иное значение, в зависимости от условия. Пожалуйста, используйте его по назначению, а для выполнения разных веток кода есть `if`.
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# Логические операторы
|
||||
|
||||
В JavaScript поддерживаются операторы `||` (ИЛИ), `&&` (И) и `!` (НЕ).
|
||||
Для операций над логическими значениями в JavaScript есть `||` (ИЛИ), `&&` (И) и `!` (НЕ).
|
||||
|
||||
Они называются *"логическими"*, но в JavaScript могут применяться к значениям любого типа и возвращают также значения любого типа.
|
||||
Хоть они и называются *"логическими"*, но в JavaScript могут применяться к значениям любого типа и возвращают также значения любого типа.
|
||||
[cut]
|
||||
|
||||
## || (ИЛИ)
|
||||
|
@ -15,7 +15,7 @@ result = a || b;
|
|||
|
||||
**Логическое ИЛИ в классическом программировании работает следующим образом: "если *хотя бы один* из аргументов `true`, то возвращает `true`, иначе -- `false`".**
|
||||
|
||||
Получается следующая таблица результатов:
|
||||
Получается следующая "таблица результатов":
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -25,9 +25,7 @@ alert( true || false); // true
|
|||
alert( false || false); // false
|
||||
```
|
||||
|
||||
При вычислении ИЛИ в JavaScript можно использовать любые значения. В этом случае они будут интерпретироваться как логические.
|
||||
|
||||
Например, число `1` будет воспринято как `true`, а `0` -- как `false`:
|
||||
Если значение не логического типа -- то оно к нему приводится. Например, число `1` будет воспринято как `true`, а `0` -- как `false`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -86,6 +84,7 @@ alert(x); // undefined, x не присвоен
|
|||
var x;
|
||||
|
||||
*!*false*/!* || (x = 1);
|
||||
|
||||
alert(x); // 1
|
||||
```
|
||||
|
||||
|
@ -93,9 +92,9 @@ alert(x); // 1
|
|||
|
||||
Итак, как мы видим, оператор ИЛИ вычисляет ровно столько значений, сколько необходимо -- до первого `true`.
|
||||
|
||||
**Оператор ИЛИ возвращает то значение, на котором остановились вычисления.**
|
||||
При этом оператор ИЛИ возвращает то значение, на котором остановились вычисления. Причём, не преобразованное к логическому типу.
|
||||
|
||||
Примеры:
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -122,6 +121,15 @@ var result = undef || zero || emptyStr || msg || 0;
|
|||
alert(result); // выведет "Привет!" - первое значение, которое является true
|
||||
```
|
||||
|
||||
Если все значения "ложные", то `||` возвратит последнее из них:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
||||
alert(undefined || '' || false || 0); // 0
|
||||
```
|
||||
|
||||
|
||||
## && (И)
|
||||
|
||||
|
||||
|
@ -131,7 +139,7 @@ alert(result); // выведет "Привет!" - первое значение
|
|||
result = a && b;
|
||||
```
|
||||
|
||||
**В классическом программировании И возвращает `true`, если оба аргумента истинны, а иначе -- `false`**
|
||||
В классическом программировании И возвращает `true`, если оба аргумента истинны, а иначе -- `false`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -141,7 +149,7 @@ alert( true && false); // false
|
|||
alert( false && false); // false
|
||||
```
|
||||
|
||||
Пример:
|
||||
Пример c `if`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -152,7 +160,7 @@ if (hour == 12 && minute == 30) {
|
|||
}
|
||||
```
|
||||
|
||||
Как и в ИЛИ, допустимы любые значения:
|
||||
Как и в ИЛИ, в И допустимы любые значения:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -163,7 +171,7 @@ if ( 1 && 0 ) { // вычислится как true && false
|
|||
|
||||
К И применим тот же принцип "короткого цикла вычислений", но немного по-другому, чем к ИЛИ.
|
||||
|
||||
**Если левый аргумент -- `false`, оператор И возвращает его и заканчивает вычисления, а иначе -- вычисляет и возвращает правый аргумент.**
|
||||
**Если левый аргумент -- `false`, оператор И возвращает его и заканчивает вычисления. Иначе -- вычисляет и возвращает правый аргумент.**
|
||||
|
||||
Например:
|
||||
|
||||
|
@ -180,6 +188,15 @@ alert(null && 5); // null
|
|||
alert(0 && "не важно"); // 0
|
||||
```
|
||||
|
||||
Можно передать и несколько значений подряд, при этом возвратится первое "ложное" (на котором остановились вычисления), а если его нет -- то последнее:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(1 && 2 && null && 3); // null
|
||||
|
||||
alert(1 && 2 && 3); // 3
|
||||
```
|
||||
|
||||
**Приоритет оператора И `&&` больше, чем ИЛИ `||`, т.е. он выполняется раньше.**
|
||||
|
||||
Поэтому в следующем коде сначала будет вычислено правое И: `1 && 0 = 0`, а уже потом -- ИЛИ.
|
||||
|
@ -189,6 +206,7 @@ alert(0 && "не важно"); // 0
|
|||
alert(5 || 1 && 0); // 5
|
||||
```
|
||||
|
||||
|
||||
[warn header="Не используйте `&&` вместо `if`"]
|
||||
|
||||
Оператор `&&` в простых случаях можно использовать вместо `if`, например:
|
||||
|
@ -200,7 +218,7 @@ var x = 1;
|
|||
(x > 0) && alert('Больше');
|
||||
```
|
||||
|
||||
Действие в правой части `&&` выполнится только в том случае, если до него дойдут вычисления. То есть, если в левой части будет `true`.
|
||||
Действие в правой части `&&` выполнится только в том случае, если до него дойдут вычисления. То есть, `alert` сработает, если в левой части будет `true`.
|
||||
|
||||
Получился аналог:
|
||||
|
||||
|
@ -213,7 +231,7 @@ if (x > 0) {
|
|||
}
|
||||
```
|
||||
|
||||
Однако, как правило, `if` лучше читается и воспринимается. Он более очевиден, поэтому лучше использовать его. Это, впрочем, относится и к другим неочевидным применениям возможностей языка.
|
||||
Однако, как правило, вариант с `if` лучше читается и воспринимается. Он более очевиден, поэтому лучше использовать его. Это, впрочем, относится и к другим неочевидным применениям возможностей языка.
|
||||
[/warn]
|
||||
|
||||
## ! (НЕ)
|
|
@ -9,7 +9,7 @@
|
|||
<li>Преобразование к логическому значению.</li>
|
||||
</ol>
|
||||
|
||||
**Эта глава описывает преобразование только примитивных значений, объекты разбираются далее в учебнике.**
|
||||
**Эта глава описывает преобразование только примитивных значений, объекты разбираются далее.**
|
||||
|
||||
|
||||
## Строковое преобразование
|
||||
|
@ -30,6 +30,8 @@ alert(a); // "true"
|
|||
alert( String(null) === "null" ); // true
|
||||
```
|
||||
|
||||
Как видно из примеров выше, преобразование происходит наиболее очевидным способом, "как есть": `false` становится `"false"`, `null` -- `"null"`, `undefined` -- `"undefined"` и т.п.
|
||||
|
||||
Также для явного преобразования применяется оператор `"+"`, у которого один из аргументов строка. В этом случае он приводит к строке и другой аргумент, например:
|
||||
|
||||
```js
|
||||
|
@ -42,7 +44,7 @@ alert( "123" + undefined); // "123undefined"
|
|||
|
||||
Численное преобразование происходит в математических функциях и выражениях, а также при сравнении данных различных типов (кроме сравнений `===`, `!==`).
|
||||
|
||||
Для преобразования к числу в явном виде можно вызвать `Number(val)`, либо, что короче, поставить перед выражением оператор унарный плюс `"+"`:
|
||||
Для преобразования к числу в явном виде можно вызвать `Number(val)`, либо, что короче, поставить перед выражением унарный плюс `"+"`:
|
||||
|
||||
```js
|
||||
var a = +"123"; // 123
|
||||
|
@ -54,14 +56,15 @@ var a = Number("123"); // 123, тот же эффект
|
|||
<tr><td>`undefined`</td><td>`NaN`</td></tr>
|
||||
<tr><td>`null`</td><td>`0`</td></tr>
|
||||
<tr><td>`true / false`</td><td>`1 / 0`</td></tr>
|
||||
<tr><td>Строка</td><td>Пробельные символы по краям обрезаются.<br>Далее, если остаётся пустая строка, то `0`.<br>Из непустой строки "считывается" число, при ошибке результат: `NaN`.</td></tr>
|
||||
<tr><td>Строка</td><td>Пробельные символы по краям обрезаются.<br>Далее, если остаётся пустая строка, то `0`, иначе из непустой строки "считывается" число, при ошибке результат `NaN`.</td></tr>
|
||||
</table>
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( +" \n 123 \n \n"); // 123
|
||||
// после обрезания пробельных символов останется "123"
|
||||
alert( +" \n 123 \n \n"); // 123
|
||||
```
|
||||
|
||||
Ещё примеры:
|
||||
|
@ -79,13 +82,12 @@ alert( +false); // 0
|
|||
|
||||
```js
|
||||
//+ run
|
||||
alert( "\n0\n" == 0 ); // true
|
||||
alert( "\n0 " == 0 ); // true
|
||||
```
|
||||
|
||||
При этом строка `"\n0\n"` преобразуется к числу -- начальные и конечные пробелы игнорируются, получается `0`.</li>
|
||||
При этом строка `"\n"` преобразуется к числу, как указано выше: начальные и конечные пробелы обрезаются, получается строка `"0"`, которая равна `0`.</li>
|
||||
</li>
|
||||
<li>
|
||||
Ещё пример сравнения разных типов:
|
||||
<li>С логическими значениями:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -118,18 +120,18 @@ alert( "1" == true );
|
|||
|
||||
```js
|
||||
//+ run
|
||||
alert(null >= 0); // true, т.к. null преобразуется к 0
|
||||
alert(null > 0); // false (не больше), т.к. null преобразуется к 0
|
||||
alert(null >= 0); // true, т.к. null преобразуется к 0
|
||||
alert(null > 0); // false (не больше), т.к. null преобразуется к 0
|
||||
alert(null == 0 ); // false (и не равен!), т.к. == рассматривает null особо.
|
||||
```
|
||||
|
||||
Значение `undefined` вообще вне сравнений:
|
||||
Значение `undefined` вообще "несравнимо":
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(undefined > 0); // false, т.к. undefined -> NaN
|
||||
alert(undefined > 0); // false, т.к. undefined -> NaN
|
||||
alert(undefined == 0); // false, т.к. это undefined (без преобразования)
|
||||
alert(undefined < 0); // false, т.к. undefined -> NaN
|
||||
alert(undefined < 0); // false, т.к. undefined -> NaN
|
||||
```
|
||||
|
||||
**Для более очевидной работы кода и во избежание ошибок лучше не давать специальным значениям участвовать в сравнениях `> >= < <=`.**
|
||||
|
@ -138,7 +140,7 @@ alert(undefined < 0); // false, т.к. undefined -> NaN
|
|||
|
||||
## Логическое преобразование
|
||||
|
||||
Преобразование к `true/false` происходит в логическом контексте, таком как `if(obj)`, `while(obj)` и при применении логических операторов.
|
||||
Преобразование к `true/false` происходит в логическом контексте, таком как `if(value)`, и при применении логических операторов.
|
||||
|
||||
Все значения, которые интуитивно "пусты", становятся `false`. Их несколько: `0`, пустая строка, `null`, `undefined` и `NaN`.
|
||||
|
||||
|
@ -172,12 +174,11 @@ alert( !!" " ); // любые непустые строки, даже из пр
|
|||
|
||||
**Два значения могут быть равны, но одно из них в логическом контексте `true`, другое -- `false`**.
|
||||
|
||||
Например, равенства в следующем примере верны, так как происходит численное преобразование:
|
||||
Например, равенство в следующем примере верно, так как происходит численное преобразование:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 0 == "\n0\n" ); // true
|
||||
alert( false == " " ); // true
|
||||
```
|
||||
|
||||
...А в логическом контексте левая часть даст `false`, правая -- `true`:
|
||||
|
@ -196,15 +197,12 @@ if ("\n0\n") {
|
|||
В JavaScript есть три преобразования:
|
||||
|
||||
<ol>
|
||||
<li>Строковое: `String(value)` -- в строковом контексте или при сложении со строкой</li>
|
||||
<li>Численное: `Number(value)` -- в численном контексте, включая унарный плюс `+value`.</li>
|
||||
<li>Строковое: `String(value)` -- в строковом контексте или при сложении со строкой. Работает очевидным образом.</li>
|
||||
<li>Численное: `Number(value)` -- в численном контексте, включая унарный плюс `+value`. Происходит при сравнении разных типов, кроме строгого равенства.</li>
|
||||
<li>Логическое: `Boolean(value)` -- в логическом контексте, можно также сделать двойным НЕ: `!!value`.</li>
|
||||
</ol>
|
||||
|
||||
**Сравнение не осуществляет преобразование типов в следующих случаях:**
|
||||
<ul>
|
||||
<li>При сравнении объектов. Две переменные, которые являются объектами равны только, когда ссылаются на один и тот же объект.</li>
|
||||
<li>При сравнении двух строк. Там отдельный алгоритм сравнения. А вот если хоть один операнд -- не строка, то значения будут приведены: `true > "000"` станет `1 > 0`.</li>
|
||||
<li>При проверке равенства с `null` и `undefined`. Они равны друг другу, но не равны чему бы то ни было ещё, этот случай прописан особо в спецификации.</li>
|
||||
</ul>
|
||||
Точные таблицы преобразований даны выше в этой главе.
|
||||
|
||||
Особым случаем является проверка равенства с `null` и `undefined`. Они равны друг другу, но не равны чему бы то ни было ещё, этот случай прописан особо в спецификации.
|
||||
|
|
@ -6,6 +6,6 @@
|
|||
|
||||
Цикл должен спрашивать число пока либо посетитель не введет число, большее `100`, либо не нажмет кнопку Cancel (ESC).
|
||||
|
||||
Предполагается, что посетитель вводит только числа.
|
||||
Предполагается, что посетитель вводит только числа, предусматривать обработку нечисловых строк в этой задаче необязательно.
|
||||
|
||||
[demo /]
|
|
@ -1,6 +1,6 @@
|
|||
# Вывести простые числа
|
||||
|
||||
[importance 4]
|
||||
[importance 3]
|
||||
|
||||
Натуральное число, большее 1, называется *простым*, если оно ни на что не делится, кроме себя и `1`.
|
||||
|
346
1-js/2-first-steps/14-while-for/article.md
Normal file
346
1-js/2-first-steps/14-while-for/article.md
Normal file
|
@ -0,0 +1,346 @@
|
|||
# Циклы 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);
|
||||
}
|
||||
```
|
||||
|
||||
Здесь:
|
||||
<ul>
|
||||
<li>**Начало:** `i=0`.</li>
|
||||
<li>**Условие:** `i<3`.</li>
|
||||
<li>**Шаг:** `i++`.</li>
|
||||
<li>**Тело:** `alert(i)`, т.е. код внутри фигурных скобок (они не обязательны, если только одна операция)</li>
|
||||
</ul>
|
||||
|
||||
Цикл выполняется так:
|
||||
|
||||
<ol>
|
||||
<li>Начало: `i=0` выполняется один-единственный раз, при заходе в цикл.</li>
|
||||
<li>Условие: `i<3` проверяется перед каждой итерацией и при входе в цикл, если оно нарушено, то происходит выход.</li>
|
||||
<li>Тело: `alert(i)`.</li>
|
||||
<li>Шаг: `i++` выполняется после *тела* на каждой итерации, но перед проверкой условия.</li>
|
||||
<li>Идти на шаг 2.</li>
|
||||
</ol>
|
||||
|
||||
Иными, словами, поток выполнения: `начало` -> (если `условие` -> `тело` -> `шаг`) -> (если `условие` -> `тело` -> `шаг`) -> ... и так далее, пока верно `условие`.
|
||||
|
||||
[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 поддерживает три вида циклов:
|
||||
<ul>
|
||||
<li>`while` -- проверка условия перед каждым выполнением.</li>
|
||||
<li>`do..while` -- проверка условия после каждого выполнения.</li>
|
||||
<li>`for` -- проверка условия перед каждым выполнением, а также дополнительные настройки.</li>
|
||||
</ul>
|
||||
|
||||
Чтобы организовать бесконечный цикл, используют конструкцию `while(true)`. При этом он, как и любой другой цикл, может быть прерван директивой `break`.
|
||||
|
||||
Если на данной итерации цикла делать больше ничего не надо, но полностью прекращать цикл не следует -- используют директиву `continue`.
|
||||
|
||||
Обе этих директивы поддерживают "метки", которые ставятся перед циклом. Метки -- единственный способ для `break/continue` повлиять на выполнение внешнего цикла.
|
||||
|
||||
Заметим, что метки не позволяют прыгнуть на произвольное место кода, в JavaScript нет такой возможности.
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
# Циклы 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);
|
||||
}
|
||||
```
|
||||
|
||||
Здесь:
|
||||
<ul>
|
||||
<li>**Начало:** `i=0`.</li>
|
||||
<li>**Условие:** `i<3`.</li>
|
||||
<li>**Шаг:** `i++`.</li>
|
||||
<li>**Тело:** `alert(i)`, т.е. код внутри фигурных скобок (они не обязательны, если только одна операция)</li>
|
||||
</ul>
|
||||
|
||||
Цикл выполняется так:
|
||||
|
||||
<ol>
|
||||
<li>Начало: `i=0` выполняется один-единственный раз, при заходе в цикл.</li>
|
||||
<li>Условие: `i<3` проверяется перед каждой итерацией и при входе в цикл, если оно нарушено, то происходит выход.</li>
|
||||
<li>Тело: `alert(i)`.</li>
|
||||
<li>Шаг: `i++` выполняется после *тела* на каждой итерации, но перед проверкой условия.</li>
|
||||
<li>Идти на шаг 2.</li>
|
||||
</ol>
|
||||
|
||||
Иными, словами, поток выполнения: `начало` -> (если `условие` -> `тело` -> `шаг`) -> (если `условие` -> `тело` -> `шаг`) -> ... и так далее, пока верно `условие`.
|
||||
|
||||
[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]
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
# Директивы break и continue
|
||||
|
||||
Для более гибкого управления циклом используются директивы `break` и `continue`.
|
||||
[cut]
|
||||
## Выход: break
|
||||
|
||||
Выйти из цикла можно не только при проверке условия но и, вообще, в любой момент. Эту возможность обеспечивает директива `break`.
|
||||
|
||||
Например, бесконечный цикл в примере прекратит выполнение при `i==5`:
|
||||
|
||||
```js
|
||||
var i=0;
|
||||
|
||||
while(1) {
|
||||
i++;
|
||||
|
||||
*!*if (i==5) break;*/!*
|
||||
|
||||
alert(i);
|
||||
}
|
||||
|
||||
alert('Последняя i = '+ i ); // 5 (*)
|
||||
```
|
||||
|
||||
Выполнение продолжится со строки `(*)`, следующей за циклом.
|
||||
|
||||
## Следующая итерация: continue [#continue]
|
||||
|
||||
Директива `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 ( checkValue(i) ) {
|
||||
// функция checkValue проверяет, подходит ли i
|
||||
|
||||
// ...
|
||||
// ... обработка
|
||||
// ... этого
|
||||
// ... значения
|
||||
// ... цикла
|
||||
// ...
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Все хорошо, но мы получили *дополнительный уровень вложенности фигурных скобок, без которого можно и нужно обойтись*.
|
||||
|
||||
Гораздо лучше здесь использовать `continue`:
|
||||
|
||||
```js
|
||||
for (var i = 0; i < 10; i++) {
|
||||
|
||||
*!*if ( !checkValue(i) ) continue;*/!*
|
||||
|
||||
// здесь мы точно знаем, что i подходит
|
||||
|
||||
// ...
|
||||
// ... обработка
|
||||
// ... этого
|
||||
// ... значения
|
||||
// ... цикла
|
||||
// ...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
[/smart]
|
||||
|
||||
[warn header="Нельзя использовать break/continue справа от оператора '?'"]
|
||||
Обычно мы можем заменить `if` на оператор вопросительный знак `'?'`.
|
||||
|
||||
То есть, запись:
|
||||
|
||||
```js
|
||||
if (условие) {
|
||||
a();
|
||||
} else {
|
||||
b();
|
||||
}
|
||||
```
|
||||
|
||||
..Аналогична записи:
|
||||
|
||||
```js
|
||||
условие ? a() : b();
|
||||
```
|
||||
|
||||
В обоих случаях в зависимости от условия выполняется либо `a()` либо `b()`.
|
||||
|
||||
Но разница состоит в том, что оператор вопросительный знак `'?'`, использованный во второй записи, возвращает значение.
|
||||
|
||||
**Синтаксические конструкции, которые не возвращают значений, нельзя использовать в операторе `'?'`.** К таким относятся большинство конструкций и, в частности, `break/continue`.
|
||||
|
||||
Поэтому такой код приведёт к ошибке:
|
||||
|
||||
```js
|
||||
(i > 5) ? alert(i) : *!*continue*/!*;
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
## Метки
|
||||
|
||||
Бывает нужно выйти одновременно из нескольких уровней цикла.
|
||||
|
||||
Представим, что нужно ввести значения точек. У каждой точки есть две координаты `(i, j)`. Цикл для ввода значений `i,j = 0..2` может выглядеть так:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
for (var i = 0; i < 3; i++) {
|
||||
|
||||
for (var j = 0; j < 3; j++) {
|
||||
|
||||
var input = prompt("Значение в координатах " + i + "," + j, "");
|
||||
|
||||
if (input == null) *!*break*/!*; // (*)
|
||||
|
||||
}
|
||||
}
|
||||
alert('Готово!');
|
||||
```
|
||||
|
||||
Здесь `break` используется, чтобы прервать ввод, если посетитель нажал на `Отмена`. Но обычный вызов `break` в строке `(*)` не может прервать два цикла сразу. Как же прервать ввод полностью? Один из способов -- поставить *метку*.
|
||||
|
||||
Метка имеет вид `"имя:"`, имя должно быть уникальным. Она ставится перед циклом, вот так:
|
||||
|
||||
```js
|
||||
outer: for (var i = 0; i < 3; i++) { ... }
|
||||
```
|
||||
|
||||
Можно также выносить ее на отдельную строку. Вызов `break outer` прерывает управление цикла с такой меткой, вот так:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
outer:
|
||||
for (var i = 0; i < 3; i++) {
|
||||
|
||||
for (var j = 0; j < 3; j++) {
|
||||
|
||||
var input = prompt('Значение в координатах '+i+','+j, '');
|
||||
|
||||
if (input == null) *!*break outer*/!*; // (*)
|
||||
|
||||
}
|
||||
}
|
||||
alert('Готово!');
|
||||
```
|
||||
|
||||
Директива `continue` также может быть использована с меткой. Управление перепрыгнет на следующую итерацию цикла с меткой.
|
||||
|
||||
**Метки можно ставить в том числе на блок, без цикла:**
|
||||
|
||||
```js
|
||||
//+ run
|
||||
my: {
|
||||
|
||||
for (;;) {
|
||||
for (i=0; i<10; i++) {
|
||||
if (i>4) break my;
|
||||
}
|
||||
}
|
||||
|
||||
some_code; // произвольный участок кода
|
||||
|
||||
}
|
||||
alert("После my"); // (*)
|
||||
```
|
||||
|
||||
В примере выше, `break` перепрыгнет через `some_code`, выполнение продолжится сразу после блока `my`, со строки `(*)`. Возможность ставить метку на блоке используется редко. Обычно метки ставятся перед циклом.
|
||||
|
||||
[smart header="Goto?"]
|
||||
В некоторых языках программирования есть оператор `goto`, который может передавать управление на любой участок программы.
|
||||
|
||||
Операторы `break/continue` более ограниченны. Они работают только внутри циклов, и метка должна быть не где угодно, а выше по уровню вложенности.
|
||||
|
||||
В JavaScript нет `goto`.
|
||||
[/smart]
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
@ -33,6 +33,8 @@ alert( "нечисло" * 2 ); // NaN, ошибка
|
|||
|
||||
Эти значения формально принадлежат типу "число", хотя, конечно, числами в их обычном понимании не являются.
|
||||
|
||||
Особенности работы с числами в JavaScript разобраны в главе [](/number).
|
||||
|
||||
## Строка "string"
|
||||
|
||||
```js
|
||||
|
@ -46,6 +48,8 @@ str = 'Одинарные кавычки тоже подойдут';
|
|||
В некоторых языках программирования есть специальный тип данных для одного символа. Например, в языке С это `char`. В JavaScript есть только тип "строка" `string`. Что, надо сказать, вполне удобно.
|
||||
[/smart]
|
||||
|
||||
Более подробно со строками мы познакомимся в главе [](/string).
|
||||
|
||||
## Булевый (логический) тип "boolean"
|
||||
|
||||
У него всего два значения: `true` (истина) и `false` (ложь).
|
||||
|
@ -101,7 +105,7 @@ alert(x); // "undefined"
|
|||
|
||||
Особняком стоит шестой тип: *"объекты"*. К нему относятся, например, даты, функции, он используется для коллекций данных и для объявления более сложных сущностей.
|
||||
|
||||
Позже мы вернёмся к этому типу и рассмотрим его принципиальные отличия от примитивов.
|
||||
Позже, в главе [про объекты](/object) мы вернёмся к этому типу и рассмотрим его принципиальные отличия от примитивов.
|
||||
|
||||
## Итого
|
||||
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
# Основные операторы
|
||||
|
||||
Для работы с переменными, со значениями, JavaScript поддерживает все стандартные операторы, большинство которых есть и в других языках программирования.
|
||||
|
||||
Несколько операторов мы знаем со школы -- это обычные сложение `+`, умножение `*`, вычитание и так далее.
|
||||
|
||||
В этой главе мы сконцентрируемся на операторах, которые в на математике не проходят и на их особенностях именно в JavaScript.
|
||||
[cut]
|
||||
|
||||
## Термины: "унарный", "бинарный", "операнд"
|
||||
|
||||
У операторов есть своя терминология, которая используется во всех языках программирования.
|
||||
|
||||
Прежде, чем мы двинемся дальше -- несколько терминов, чтобы понимать, о чём речь.
|
||||
|
||||
<ul>
|
||||
<li>*Операнд* -- то, к чему применяется оператор. Например: `5 * 2` -- оператор умножения с левым и правым операндами. Другое название: "аргумент оператора".</li>
|
||||
<li>*Унарным* называется оператор, который применяется к одному выражению. Например, оператор унарный минус `"-"` меняет знак числа на противоположный:
|
||||
|
@ -13,9 +20,11 @@
|
|||
```js
|
||||
//+ run
|
||||
var x = 1;
|
||||
alert( -x ); // -1, унарный минус
|
||||
alert( -(x+2) ); // -3, унарный минус применён к результату сложения x+2
|
||||
alert( -(-3) ); // 3
|
||||
|
||||
*!*
|
||||
x = -x;
|
||||
*/!*
|
||||
alert( x ); // -1, применили унарный минус
|
||||
```
|
||||
|
||||
</li>
|
||||
|
@ -26,54 +35,23 @@ alert( -(-3) ); // 3
|
|||
var x = 1, y = 3;
|
||||
alert( y - x ); // 2, бинарный минус
|
||||
```
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Некоторые операторы, например, вычитание `"-"` и сложение `"+"`, бывают в двух вариантах: унарный -- при применении к одному операнду, и бинарный -- к двум.
|
||||
|
||||
## Арифметические операторы
|
||||
## Сложение строк, бинарный +
|
||||
|
||||
Базовые арифметические операторы знакомы нам с детства: это плюс `+`, минус `-`, умножить `*`, поделить `/`.
|
||||
Обычно при помощи плюса `'+'` складывают числа.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(2 + 2); // 4
|
||||
```
|
||||
|
||||
Или чуть сложнее:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 2;
|
||||
|
||||
i = (2 + i) * 3 / i;
|
||||
|
||||
alert(i); // 6
|
||||
```
|
||||
|
||||
**Более редкий арифметический оператор `%` интересен тем, что никакого отношения к процентам не имеет. Его результат `a % b` -- это остаток от деления `a` на `b`.**
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(5 % 2); // 1, остаток от деления 5 на 2
|
||||
alert(8 % 3); // 2, остаток от деления 8 на 3
|
||||
alert(6 % 3); // 0, остаток от деления 6 на 3
|
||||
```
|
||||
|
||||
### Сложение строк, бинарный +
|
||||
|
||||
Если бинарный оператор `+` применить к строкам, то он их объединяет в одну:
|
||||
Но если бинарный оператор `'+'` применить к строкам, то он их объединяет в одну:
|
||||
|
||||
```js
|
||||
var a = "моя" + "строка";
|
||||
alert(a); // моястрока
|
||||
```
|
||||
|
||||
Иначе говорят, что "плюс производит конкатенацию (сложение) строк".
|
||||
|
||||
**Если хотя бы один аргумент является строкой, то второй будет также преобразован к строке!**
|
||||
|
||||
Причем не важно, справа или слева находится операнд-строка, в любом случае нестроковый аргумент будет преобразован. Например:
|
||||
|
@ -84,12 +62,6 @@ alert( '1' + 2 ); // "12"
|
|||
alert( 2 + '1' ); // "21"
|
||||
```
|
||||
|
||||
[smart]
|
||||
Для сложения строк в программировании используется специальный термин "конкатенация" (от англ. concat).
|
||||
|
||||
В примерах выше оператор `'+'` производил конкатенацию строк.
|
||||
[/smart]
|
||||
|
||||
**Это приведение к строке -- особенность исключительно бинарного оператора `"+"`.**
|
||||
|
||||
Остальные арифметические операторы работают только с числами и всегда приводят аргументы к числу.
|
||||
|
@ -98,18 +70,20 @@ alert( 2 + '1' ); // "21"
|
|||
|
||||
```js
|
||||
//+ run
|
||||
alert( '1' - 2 ); // -1
|
||||
alert( 6 / '2'); // 3
|
||||
alert( 2 - '1' ); // 1
|
||||
alert( 6 / '2'); // 3
|
||||
```
|
||||
|
||||
### Унарный плюс +
|
||||
|
||||
Унарный, то есть применённый к одному значению, плюс как арифметический оператор ничего не делает:
|
||||
|
||||
### Преобразование к числу, унарный плюс +
|
||||
|
||||
Унарный, то есть применённый к одному значению, плюс ничего не делает с числами:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( +1 ); // 1
|
||||
alert( +(1-2) ); // -1
|
||||
alert( +(1 - 2) ); // -1
|
||||
```
|
||||
|
||||
Как видно, плюс ничего не изменил в выражениях. Результат -- такой же, как и без него.
|
||||
|
@ -118,52 +92,69 @@ alert( +(1-2) ); // -1
|
|||
|
||||
Например, когда мы получаем значения из HTML-полей или от пользователя, то они обычно в форме строк.
|
||||
|
||||
А что, если их нужно сложить? Бинарный плюс сложит их как строки:
|
||||
А что, если их нужно, к примеру, сложить? Бинарный плюс сложит их как строки:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = "2";
|
||||
var b = "3";
|
||||
var apples = "2";
|
||||
var oranges = "3";
|
||||
|
||||
alert( a + b ); // "23", так как бинарный плюс складывает строки
|
||||
alert( apples + oranges ); // "23", так как бинарный плюс складывает строки
|
||||
```
|
||||
|
||||
Поэтому используем унарный плюс, чтобы преобразовать к числу:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = "2";
|
||||
var b = "3";
|
||||
var apples = "2";
|
||||
var oranges = "3";
|
||||
|
||||
alert( +a + +b); // 5, число, оба операнда предварительно преобразованы в числа
|
||||
alert( +apples + +oranges); // 5, число, оба операнда предварительно преобразованы в числа
|
||||
```
|
||||
|
||||
С точки зрения математики такое изобилие плюсов может показаться странном. С точки зрения программирования -- никаких разночтений: сначала выполнятся унарные плюсы, приведут строки к числам, а затем -- бинарный `'+'` их сложит.
|
||||
|
||||
Почему унарные плюсы выполнились до бинарного сложения? Как мы сейчас увидим, дело в их приоритете.
|
||||
|
||||
## Приоритет
|
||||
|
||||
|
||||
В том случае, если в выражении есть несколько операторов -- порядок их выполнения определяется *приоритетом*.
|
||||
|
||||
Из школы мы знаем, что умножение в выражении `2 * 2 + 1` выполнится раньше сложения, т.к. его *приоритет* выше, а скобки явно задают порядок выполнения. Но в JavaScript -- гораздо больше операторов, поэтому существует целая [таблица приоритетов](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence).
|
||||
|
||||
Она содержит как уже пройденные операторы, так и те, которые мы еще не проходили. В ней каждому оператору задан числовой приоритет. Тот, у кого число меньше -- выполнится раньше. Если приоритет одинаковый, то порядок выполнения -- слева направо.
|
||||
|
||||
Отрывок из таблицы:
|
||||
|
||||
<table>
|
||||
<tr><td>...</td><td>...</td><td>...</td></tr>
|
||||
<tr><td>4</td><td>унарный плюс</td><td>`+`</td></tr>
|
||||
<tr><td>4</td><td>унарный минус</td><td>`-`</td></tr>
|
||||
<tr><td>5</td><td>умножение</td><td>`*`</td></tr>
|
||||
<tr><td>5</td><td>деление</td><td>`/`</td></tr>
|
||||
<tr><td>6</td><td>сложение</td><td>`+`</td></tr>
|
||||
<tr><td>6</td><td>вычитание</td><td>`-`</td></tr>
|
||||
<tr><td>17</td><td>присвоение</td><td>`=`</td></tr>
|
||||
<tr><td>...</td><td>...</td><td>...</td></tr>
|
||||
</table>
|
||||
|
||||
Так как "унарный плюс" имеет приоритет `4`, выше, чем `6` у обычного "сложения", то в выражении `+apples + +oranges` сначала сработали плюсы у `apples` и `oranges`, а затем уже обычное сложение.
|
||||
|
||||
## Присваивание
|
||||
|
||||
Оператор присваивания выглядит как знак равенства `=`:
|
||||
Обратим внимание, в таблице приоритетов также есть оператор присваивания `=`.
|
||||
|
||||
У него -- один из самых низких приоритетов: `17`.
|
||||
|
||||
Именно поэтому, когда переменную чему-либо присваивают, например, `x = 2 * 2 + 1` сначала выполнится арифметика, а уже затем -- произойдёт присвоение `=`.
|
||||
|
||||
```js
|
||||
var i = 1 + 2;
|
||||
var x = 2 * 2 + 1;
|
||||
|
||||
alert(i); // 3
|
||||
alert(x); // 5
|
||||
```
|
||||
|
||||
Он вычисляет выражение, которое находится справа, и присваивает результат переменной. Это выражение может быть достаточно сложным и включать в себя любые другие переменные:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
|
||||
*!*
|
||||
a = b + a + 3; // (*)
|
||||
*/!*
|
||||
|
||||
alert(a); // 6
|
||||
```
|
||||
|
||||
В строке `(*)` сначала произойдет вычисление, использующее текущее значение `a` (т.е. `1`), после чего результат перезапишет старое значение `a`.
|
||||
|
||||
**Возможно присваивание по цепочке:**
|
||||
|
||||
```js
|
||||
|
@ -182,7 +173,9 @@ alert(c); // 4
|
|||
Такое присваивание работает справа-налево, то есть сначала вычислятся самое правое выражение `2+2`, присвоится в `c`, затем выполнится `b = c` и, наконец, `a = b`.
|
||||
|
||||
[smart header="Оператор `\"=\"` возвращает значение"]
|
||||
Все операторы возвращают значение. Вызов `x = выражение` записывает выражение в `x`, а затем возвращает его. Благодаря этому присваивание можно использовать как часть более сложного выражения:
|
||||
Все операторы возвращают значение. Вызов `x = выражение` не является исключением.
|
||||
|
||||
Он записывает выражение в `x`, а затем возвращает его. Благодаря этому присваивание можно использовать как часть более сложного выражения:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -205,29 +198,21 @@ alert(c); // 0
|
|||
[/smart]
|
||||
|
||||
|
||||
## Приоритет
|
||||
## Взятие остатка %
|
||||
|
||||
В том случае, если в выражении есть несколько операторов - порядок их выполнения определяется *приоритетом*.
|
||||
Оператор взятия остатка `%` интересен тем, что, несмотря на обозначение, никакого отношения к процентам не имеет.
|
||||
|
||||
Из школы мы знаем, что умножение в выражении `2 * 2 + 1` выполнится раньше сложения, т.к. его *приоритет* выше, а скобки явно задают порядок выполнения. Но в JavaScript -- гораздо больше операторов, поэтому существует целая [таблица приоритетов](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence).
|
||||
Его результат `a % b` -- это остаток от деления `a` на `b`.
|
||||
|
||||
Она содержит как уже пройденные операторы, так и те, которые мы еще не проходили. В ней каждому оператору задан числовой приоритет. Тот, у кого число меньше -- выполнится раньше. Если приоритет одинаковый, то порядок выполнения -- слева направо.
|
||||
Например:
|
||||
|
||||
Отрывок из таблицы:
|
||||
```js
|
||||
//+ run
|
||||
alert(5 % 2); // 1, остаток от деления 5 на 2
|
||||
alert(8 % 3); // 2, остаток от деления 8 на 3
|
||||
alert(6 % 3); // 0, остаток от деления 6 на 3
|
||||
```
|
||||
|
||||
<table>
|
||||
<tr><td>...</td><td>...</td><td>...</td></tr>
|
||||
<tr><td>5</td><td>умножение</td><td>`*`</td></tr>
|
||||
<tr><td>5</td><td>деление</td><td>`/`</td></tr>
|
||||
<tr><td>6</td><td>сложение</td><td>`+`</td></tr>
|
||||
<tr><td>6</td><td>вычитание</td><td>`-`</td></tr>
|
||||
<tr><td>17</td><td>присвоение</td><td>`=`</td></tr>
|
||||
<tr><td>...</td><td>...</td><td>...</td></tr>
|
||||
</table>
|
||||
|
||||
Посмотрим на таблицу в действии.
|
||||
|
||||
В выражении `x = 2 * 2 + 1` есть три оператора: присвоение `=`, умножение `*` и сложение `+`. Приоритет умножения `*` равен `5`, оно выполнится первым, затем произойдёт сложение `+`, у которого приоритет `6`, и после них -- присвоение `=`, с приоритетом 17.
|
||||
|
||||
## Инкремент/декремент: ++, --
|
||||
|
||||
|
@ -324,7 +309,7 @@ alert( i++ ); // 0
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
**Инкремент/декремент можно использовать в любых выражениях.**
|
||||
[smart header="Инкремент/декремент можно использовать в любых выражениях"]
|
||||
|
||||
При этом он имеет более высокий приоритет и выполняется раньше, чем арифметические операции:
|
||||
|
||||
|
@ -352,6 +337,7 @@ var i = 1;
|
|||
alert( 2 * i );
|
||||
i++;
|
||||
```
|
||||
[/smart]
|
||||
|
||||
## Побитовые операторы
|
||||
|
||||
|
@ -370,11 +356,11 @@ i++;
|
|||
<li>ZERO-FILL RIGHT SHIFT(правый сдвиг с заполнением нулями) ( `>>>` )</li>
|
||||
</ul>
|
||||
|
||||
Вы можете более подробно почитать о них в отдельной статье [](/bitwise-operators).
|
||||
Они используются редко, поэтому вынесены в отдельную главу [](/bitwise-operators).
|
||||
|
||||
|
||||
|
||||
## Вызов операторов с присваиванием
|
||||
## Сокращённая арифметика с присваиванием
|
||||
|
||||
Часто нужно применить оператор к переменной и сохранить результат в ней же, например:
|
||||
|
||||
|
@ -384,9 +370,7 @@ n = n + 5;
|
|||
n = n * 2;
|
||||
```
|
||||
|
||||
Эту запись можно укоротить при помощи совмещённых операторов: <code>+=, -=, *=, /=, >>=, <<=, >>>=, &=, |=, ^=</code>.
|
||||
|
||||
Вот так:
|
||||
Эту запись можно укоротить при помощи совмещённых операторов, вот так:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -397,13 +381,24 @@ n *= 2; // теперь n=14 (работает как n = n * 2)
|
|||
alert(n); // 14
|
||||
```
|
||||
|
||||
Все эти операторы имеют в точности такой же приоритет, как обычное присваивание, то есть выполняются после большинства других операций.
|
||||
Так можно сделать для операторов `+,-,*,/` и бинарных `<<,>>,>>>,&,|,^`.
|
||||
|
||||
Вызов с присваиванием имеет в точности такой же приоритет, как обычное присваивание, то есть выполнится после большинства других операций:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var n = 2;
|
||||
n *= 3 + 5;
|
||||
|
||||
alert(n); // 16 (n = 2 * 8)
|
||||
```
|
||||
|
||||
|
||||
## Оператор запятая
|
||||
|
||||
Запятая тоже является оператором. Ее можно вызвать явным образом, например:
|
||||
Один из самых необычных операторов -- запятая `','`.
|
||||
|
||||
Его можно вызвать явным образом, например:
|
||||
|
||||
```js
|
||||
//+ run
|
|
@ -1,20 +1,22 @@
|
|||
# Операторы сравнения и логические значения
|
||||
|
||||
В этом разделе мы познакомимся с операторами сравнения и с логическими значениями, которые такие операторы возвращают.
|
||||
|
||||
[cut]
|
||||
Многие операторы сравнения знакомы нам со школы:
|
||||
|
||||
Многие операторы сравнения знакомы нам из математики:
|
||||
|
||||
<ul>
|
||||
<li>Больше/меньше: <code>a > b</code>, <code>a < b</code>.</li>
|
||||
<li>Больше/меньше или равно: <code>a >= b</code>, <code>a <= b</code>.</li>
|
||||
<li>Равно `a == b`.
|
||||
Для сравнения используется два символа равенства `'='`. Один символ `a = b` означал бы присваивание.</li>
|
||||
<li>"Не равно". В школе он пишется как <code>≠</code>, в JavaScript -- знак равенства с восклицательным знаком перед ним <code>!=</code>.</li>
|
||||
<li>"Не равно". В математике он пишется как <code>≠</code>, в JavaScript -- знак равенства с восклицательным знаком перед ним <code>!=</code>.</li>
|
||||
</ul>
|
||||
|
||||
## Логические значения
|
||||
|
||||
Как и другие операторы, сравнение возвращает значение. Это значение имеет специальный *логический* тип.
|
||||
Как и другие операторы, сравнение возвращает значение. Это значение имеет *логический* тип.
|
||||
|
||||
Существует всего два логических значения:
|
||||
<ul>
|
||||
|
@ -35,12 +37,12 @@ alert( 2 != 1 ); // true
|
|||
|
||||
```js
|
||||
//+ run
|
||||
var a = true; // присвоили явно
|
||||
var b = 3 > 4; // false
|
||||
var a = true; // присваивать явно
|
||||
|
||||
var b = 3 > 4; // или как результат сравнения
|
||||
alert( b ); // false
|
||||
|
||||
alert( a == b ); // (true == false) неверно, результат false
|
||||
alert( a == b ); // (true == false) неверно, выведет false
|
||||
```
|
||||
|
||||
## Сравнение строк
|
||||
|
@ -141,18 +143,28 @@ alert( false == 0 ); // true, false становится 0, а true 1.
|
|||
|
||||
## Строгое равенство
|
||||
|
||||
Обычное равенство не может отличить `0` от `false`:
|
||||
В обычном операторе `==` есть "проблема"" -- он не может отличить `0` от `false`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(0 == false); // true, так как false преобразуется к 0
|
||||
alert(0 == false); // true
|
||||
```
|
||||
|
||||
Та же ситуация с пустой строкой:
|
||||
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert('' == false); // true
|
||||
```
|
||||
|
||||
Это естественное следствие того, что операнды разных типов преобразовались к числу. Пустая строка, как и `false`, при преобразовании к числу дают `0`.
|
||||
|
||||
Что же делать, если всё же нужно отличить `0` от `false`?
|
||||
|
||||
**Для проверки равенства без преобразования типов используются операторы строгого равенства `===` (тройное равно) и `!==`.**
|
||||
|
||||
Они сравнивают без приведения типов. Если тип разный, то такие значения всегда неравны (строго):
|
||||
Если тип разный, то они всегда возвращают `false`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -175,7 +187,7 @@ alert(0 === false); // false, т.к. типы различны
|
|||
|
||||
Посмотрим забавные следствия.
|
||||
|
||||
[smart header="Некорректный результат сравнения `null` с `0`"]
|
||||
### Некорректный результат сравнения null с 0
|
||||
Сравним `null` с нулём:
|
||||
|
||||
```js
|
||||
|
@ -199,9 +211,8 @@ alert(null >= 0); // *!*true*/!*
|
|||
|
||||
В результате получается странная с точки зрения здравого смысла ситуация, которую мы видели в примере выше.
|
||||
|
||||
[/smart]
|
||||
### Несравнимый undefined
|
||||
|
||||
[smart header="Несравнимый `undefined`"]
|
||||
Значение `undefined` вообще нельзя сравнивать:
|
||||
|
||||
```js
|
||||
|
@ -215,7 +226,7 @@ alert(undefined == 0); // false (3)
|
|||
<li>Сравнения `(1)` и `(2)` дают `false` потому, что `undefined` при преобразовании к числу даёт `NaN`. А значение `NaN` по стандарту устроено так, что сравнения `==`, `<`, `>`, `<=`, `>=` и даже `===` с ним возвращают `false`.</li>
|
||||
<li>Проверка равенства `(3)` даёт `false`, потому что в стандарте явно прописано, что `undefined` равно лишь `null` и ничему другому.</li>
|
||||
</ul>
|
||||
[/smart]
|
||||
|
||||
|
||||
**Вывод: любые сравнения с `undefined/null`, кроме точного `===`, следует делать с осторожностью.**
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Побитовые операторы интерпретируют операнды как последовательность из 32 битов (нулей и единиц). Они производят операции, используя двоичное представление числа, и возвращают новую последовательность из 32 бит (число) в качестве результата.
|
||||
|
||||
**Эта глава сложная, требует дополнительных знаний в программировании и не очень важная, вы можете пропустить её.**
|
||||
Эта глава требует дополнительных знаний в программировании и не очень важная, при первом чтении вы можете пропустить её и вернуться потом, когда захотите понять, как побитовые операторы работают.
|
||||
[cut]
|
||||
|
||||
## Формат 32-битного целого числа со знаком [#signed-format]
|
||||
|
@ -31,11 +31,6 @@ a = 255;// 00000000000000000000000011111111
|
|||
|
||||
Обратите внимание, каждое число состоит ровно из 32-битов.
|
||||
|
||||
[smart header="Младший бит слева"]
|
||||
Несмотря на то, что нам такой способ записи чисел кажется не совсем обычным, бывают языки и технологии, использующие способ записи "младший бит слева", когда биты пишутся наоборот, от меньшего разряда к большему.
|
||||
|
||||
Именно поэтому спецификация EcmaScript явно говорит "старший бит слева".
|
||||
[/smart]
|
||||
|
||||
</li>
|
||||
<li>*Дополнение до двойки* -- это название способа поддержки отрицательных чисел.
|
||||
|
@ -117,8 +112,6 @@ a = 255;// 00000000000000000000000011111111
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
## Описание работы операторов
|
||||
|
||||
Побитовые операторы работают следующим образом:
|
||||
|
||||
<ol>
|
||||
|
@ -129,7 +122,33 @@ a = 255;// 00000000000000000000000011111111
|
|||
|
||||
Посмотрим, как работают операторы, на примерах.
|
||||
|
||||
### & (Побитовое И)
|
||||
[smart header="Вспомогательные функции parseInt, toString"]
|
||||
|
||||
Для удобной работы с примерами в этой статье, если вы захотите протестировать что-то в консоли, пригодятся две функции.
|
||||
|
||||
<ul>
|
||||
<li>`parseInt("11000", 2)` -- переводит строку с двоичной записью числа в число.</li>
|
||||
<li>`n.toString(2)` -- получает для числа `n` запись в 2-ной системе в виде строки.</li>
|
||||
</ul>
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var access = parseInt("11000", 2); // получаем число из строки
|
||||
|
||||
alert(access); // 24, число с таким 2-ным представлением
|
||||
|
||||
var access2 = access.toString(2); // обратно двоичную строку из числа
|
||||
|
||||
alert(access2); // 11000
|
||||
```
|
||||
|
||||
Без них перевод в двоичную систему и обратно был бы куда менее удобен.
|
||||
Более подробно они разбираются в главе [](/number).
|
||||
[/smart]
|
||||
|
||||
## & (Побитовое И)
|
||||
|
||||
Выполняет операцию И над каждой парой бит.
|
||||
|
||||
|
@ -158,7 +177,7 @@ a = 255;// 00000000000000000000000011111111
|
|||
= 8 (по осн. 10)
|
||||
```
|
||||
|
||||
### | (Побитовое ИЛИ)
|
||||
## | (Побитовое ИЛИ)
|
||||
|
||||
Выполняет операцию ИЛИ над каждой парой бит. Результат `a | b` равен 1, если хотя бы один бит из <code>a,b</code> равен 1.
|
||||
|
||||
|
@ -185,7 +204,7 @@ a = 255;// 00000000000000000000000011111111
|
|||
= 15 (по осн. 10)
|
||||
```
|
||||
|
||||
### ^ (Исключающее ИЛИ)
|
||||
## ^ (Исключающее ИЛИ)
|
||||
|
||||
Выполняет операцию "Исключающее ИЛИ" над каждой парой бит.
|
||||
|
||||
|
@ -271,9 +290,7 @@ a = 255;// 00000000000000000000000011111111
|
|||
[/smart]
|
||||
|
||||
|
||||
|
||||
|
||||
### ~ (Побитовое НЕ)
|
||||
## ~ (Побитовое НЕ)
|
||||
|
||||
Производит операцию НЕ над каждым битом, заменяя его на обратный ему.
|
||||
|
||||
|
@ -429,14 +446,21 @@ alert( 100 >> 3 ); // 12, деление на 2 три раза, целая ча
|
|||
|
||||
### Маска
|
||||
|
||||
Для этого примера представим, что наш скрипт работает с пользователями:
|
||||
Для этого примера представим, что наш скрипт работает с пользователями.
|
||||
|
||||
У них могут быть различные роли в проекте:
|
||||
|
||||
<ul>
|
||||
<li>`Гость`</li>
|
||||
<li>`Редактор`</li>
|
||||
<li>`Админ`</li>
|
||||
</ul>
|
||||
|
||||
У каждого из них есть ряд доступов, которые можно свести в таблицу:
|
||||
Каждой роли соответствует ряд доступов к статьям и функционалу сайта.
|
||||
|
||||
Например, `Гость` может лишь просматривать статьи сайта, а `Редактор` -- ещё и редактировать их, и тому подобное.
|
||||
|
||||
Что-то в таком духе:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
|
@ -514,7 +538,11 @@ alert( 100 >> 3 ); // 12, деление на 2 три раза, целая ча
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
**Мы "упаковали" много информации в одно число. Это экономит память. Но, кроме этого, по нему очень легко проверить, имеет ли посетитель заданную *комбинацию доступов*!**
|
||||
В последней колонке находится десятичное число, которое получится, если прочитать строку доступов в двоичном виде.
|
||||
|
||||
Например, доступ гостя `10100 = 20`.
|
||||
|
||||
Такая интерпретация доступов позволяет "упаковать" много информации в одно число. Это экономит память, а кроме этого -- это удобно, поскольку в дополнение к экономии -- по такому значению очень легко проверить, имеет ли посетитель заданную *комбинацию доступов*!
|
||||
|
||||
Для этого посмотрим, как в 2-ной системе представляется каждый доступ в отдельности.
|
||||
|
||||
|
@ -526,113 +554,63 @@ alert( 100 >> 3 ); // 12, деление на 2 три раза, целая ча
|
|||
<li>Доступ, соответствующий только просмотру статей: `10000 (=16)`.</li>
|
||||
</ul>
|
||||
|
||||
Например, просматривать и изменять статьи позволит доступ `access = 11000`.
|
||||
Доступ одновременно на просмотр и изменение статей -- это двоичное число с `1` на соответствующих позициях, то есть `access = 11000`.
|
||||
|
||||
Как правило, доступы задаются в виде констант:
|
||||
|
||||
```js
|
||||
var ACCESS_ADMIN = 1; // 00001
|
||||
var ACCESS_GOODS_CHANGE = 2; // 00010
|
||||
var ACCESS_GOODS_EDIT = 2; // 00010
|
||||
var ACCESS_GOODS_VIEW = 4; // 00100
|
||||
var ACCESS_ARTICLE_CHANGE = 8; // 01000
|
||||
var ACCESS_ARTICLE_EDIT = 8; // 01000
|
||||
var ACCESS_ARTICLE_VIEW = 16; // 10000
|
||||
```
|
||||
|
||||
Из этих констант получить нужную комбинацию доступов можно при помощи операции `|`.
|
||||
|
||||
```js
|
||||
var access = ACCESS_ARTICLE_VIEW | ACCESS_ARTICLE_CHANGE; // 11000
|
||||
var guest = ACCESS_ARTICLE_VIEW | ACCESS_GOODS_VIEW; // 10100
|
||||
var editor = guest | ACCESS_ARTICLE_EDIT | ACCESS_GOODS_EDIT; // 11110
|
||||
var admin = editor | ACCESS_ADMIN; // 11111
|
||||
```
|
||||
|
||||
### Двоичные числа в JavaScript
|
||||
Теперь, чтобы понять, есть ли в доступе `editor` нужный доступ, например управление правами -- достаточно применить к нему побитовый оператор И (`&`) с соответствующей константой.
|
||||
|
||||
Для удобной работы с примерами в этой статье пригодятся две функции.
|
||||
Ненулевой результат будет означать, что доступ есть:
|
||||
|
||||
<ul>
|
||||
<li>`parseInt("11000", 2)` -- переводит строку с двоичной записью числа в число.</li>
|
||||
<li>`n.toString(2)` -- получает для числа `n` запись в 2-ной системе в виде строки.</li>
|
||||
</ul>
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var access = parseInt("11000", 2); // получаем число из строки
|
||||
|
||||
alert(access); // 24, число с таким 2-ным представлением
|
||||
|
||||
var access2 = access.toString(2); // обратно двоичную строку из числа
|
||||
|
||||
alert(access2); // 11000
|
||||
```
|
||||
|
||||
### Проверка доступов
|
||||
|
||||
Для того, чтобы понять, есть ли в доступе `access` нужный доступ, например управление правами -- достаточно применить к нему побитовый оператор И (`&`) с соответствующей маской.
|
||||
|
||||
Создадим для примера ряд доступов и проверим их:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var access = parseInt("11111", 2); // 31, все 1 означает, что доступ полный
|
||||
|
||||
alert(access & ACCESS_ADMIN); // если результат не 0, то есть доступ ACCESS_ADMIN
|
||||
```
|
||||
|
||||
А теперь та же проверка для посетителя с другими правами:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var access = parseInt("10100"); // 20, нет 1 в конце
|
||||
|
||||
alert(access & ACCESS_ADMIN); // 0, нет доступа к управлению правами
|
||||
alert(editor & ACCESS_ADMIN); // 0, доступа нет
|
||||
alert(editor & ACCESS_ARTICLE_EDIT); // 8, доступ есть
|
||||
```
|
||||
|
||||
Такая проверка работает, потому что оператор И ставит `1` на те позиции результата, на которых в обоих операндах стоит `1`.
|
||||
|
||||
Так что `access & 1` для любого числа `access` поставит все биты в ноль, кроме самого правого. А самый правый станет `1` только если он равен `1` в `access`.
|
||||
|
||||
Для полноты картины также проверим, даёт ли доступ `11111` право на изменение товаров. Для этого нужно применить к доступу оператор И (`&`) с `00010` (=`2` в 10-ной системе).
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var adminAccess = 31; // 111*!*1*/!*1
|
||||
|
||||
alert(adminAccess & ACCESS_GOODS_CHANGE); // не 0, есть доступ к изменению товаров
|
||||
```
|
||||
|
||||
**Можно проверить один из нескольких доступов.**
|
||||
|
||||
Например, проверим, есть ли права на просмотр ИЛИ изменение товаров. Соответствующие права задаются битом `1` на втором и третьем месте с конца, что даёт число `00110` (=`6` в 10-ной системе).
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var check = ACCESS_GOODS_VIEW | ACCESS_GOODS_CHANGE; // 6, 00110
|
||||
var check = ACCESS_GOODS_VIEW | ACCESS_GOODS_EDIT; // 6, 00110
|
||||
|
||||
var access = 30; // 11*!*11*/!*0;
|
||||
|
||||
alert(access & check); // не 0, значит есть доступ к просмотру ИЛИ изменению
|
||||
|
||||
access = parseInt("11100", 2);
|
||||
|
||||
alert(access & check); // не 0, есть доступ к просмотру ИЛИ изменению
|
||||
alert(admin & check); // не 0, значит есть доступ к просмотру ИЛИ изменению
|
||||
```
|
||||
|
||||
Как видно из примера выше, если в аргументе `check` стоит ИЛИ из нескольких доступов `ACCESS_*`, то и результат проверки скажет, есть ли хотя бы один из них. А какой -- нужно смотреть отдельной проверкой, если это важно.
|
||||
*Битовой маской* называют как раз комбинацию двоичных значений (`check` в примере выше), которая используется для проверки и выборки единиц на нужных позициях.
|
||||
|
||||
**Итак, маска даёт возможность удобно "паковать" много битовых значений в одно число при помощи ИЛИ `|`, а также, при помощи оператора И (`&`), проверять маску на комбинацию установленных битов.**
|
||||
Маски могут быть весьма удобны.
|
||||
|
||||
### Маски в функциях
|
||||
|
||||
Зачастую маски используют в функциях, чтобы одним параметром передать несколько "флагов", т.е. однобитных значений.
|
||||
|
||||
Например:
|
||||
В частности, их используют в функциях, чтобы одним параметром передать несколько "флагов", т.е. однобитных значений.
|
||||
|
||||
Пример вызова функции с маской:
|
||||
```js
|
||||
// найти пользователей с правами на изменение товаров или администраторов
|
||||
findUsers(ACCESS_GOODS_CHANGE | ACCESS_ADMIN);
|
||||
findUsers(ACCESS_GOODS_EDIT | ACCESS_ADMIN);
|
||||
```
|
||||
|
||||
Это довольно-таки коротко и элегантно, но, вместе с тем, применение масок налагает определённые ограничения. В частности, побитовые операторы в JavaScript работают только с 32-битными числами, а значит, к примеру, 33 доступа уже в число не упакуешь. Да и работа с двоичной системой счисления -- как ни крути, менее удобна, чем с десятичной или с обычными логическими значениями `true/false`.
|
||||
|
||||
Поэтому основная сфера применения масок -- это быстрые вычисления, экономия памяти, низкоуровневые операции, связанные с рисованием из JavaScript (3d-графика), интеграция с некоторыми функциями ОС (для серверного JavaScript), и другие ситуации, когда уже существуют функции, требующие битовую маску.
|
||||
|
||||
### Округление
|
||||
|
||||
Так как битовые операции отбрасывают десятичную часть, то их можно использовать для округления. Достаточно взять любую операцию, которая не меняет значение числа.
|
||||
|
@ -667,7 +645,7 @@ alert( 1.1 + 1.2 ^ 0 ); // 2, сложение выполнится раньше
|
|||
|
||||
### Проверка на -1
|
||||
|
||||
[Внутренний формат](#signed-format) чисел устроен так, что для смены знака нужно все биты заменить на противоположные ("обратить") и прибавить `1`.
|
||||
[Внутренний формат](#signed-format) 32-битных чисел устроен так, что для смены знака нужно все биты заменить на противоположные ("обратить") и прибавить `1`.
|
||||
|
||||
Обращение битов -- это побитовое НЕ (`~`). То есть, при таком формате представления числа `-n = ~n + 1`. Или, если перенести единицу: `~n = -(n+1)`.
|
||||
|
||||
|
@ -706,7 +684,7 @@ if (~str.indexOf("верка")) { // Сочетание "if (~...indexOf)" чи
|
|||
|
||||
### Умножение и деление на степени 2
|
||||
|
||||
Оператор `a << b`, сдвигая биты, по сути умножает `a` на <code>2<sup>b</sup></code>.
|
||||
Оператор `a << b`, сдвигая биты, по сути умножает `a` на `2` в степени `b`.
|
||||
|
||||
Например:
|
||||
|
||||
|
@ -719,7 +697,7 @@ alert( 3 << 3 ); // 3*(2*2*2) = 24
|
|||
|
||||
При этом следует иметь в виду, что максимальный верхний порог такого умножения меньше, чем обычно, так как побитовый оператор оперирует 32-битными целыми, в то время как обычные операторы оперируют числами длиной 64 бита.
|
||||
|
||||
**Оператор `a >> b`, сдвигая биты, производит целочисленное деление `a` на <code>2<sup>b</sup></code>.**
|
||||
Оператор сдвига в другую сторону `a >> b`, производит обратную операцию -- целочисленное деление `a` на <code>2<sup>b</sup></code>.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -736,17 +714,18 @@ alert( 11 >> 2 ); // 2, целочисленное деление (менее з
|
|||
|
||||
Как правило, битовое представление числа используется для:
|
||||
<ul>
|
||||
<li>Упаковки нескольких битововых значений ("флагов") в одно значение. Это экономит память и позволяет проверять наличие комбинации флагов одним оператором `&`. Кроме того, такое упакованное значение будет для функции всего одним параметром, это тоже удобно.</li>
|
||||
<li>Округления числа: `(12.34^0) = 12`.</li>
|
||||
<li>Проверки на равенство `-1`: `if (~n) { n не -1 }`.</li>
|
||||
<li>Упаковки нескольких битововых значений ("флагов") в одно значение. Это экономит память и позволяет проверять наличие комбинации флагов одним оператором `&`.</li>
|
||||
<li>Других ситуаций, когда нужны битовые маски.</li>
|
||||
</ul>
|
||||
[head]
|
||||
<script>
|
||||
|
||||
var ACCESS_ADMIN = 1; // 00001
|
||||
var ACCESS_GOODS_CHANGE = 2; // 00010
|
||||
var ACCESS_GOODS_EDIT = 2; // 00010
|
||||
var ACCESS_GOODS_VIEW = 4; // 00100
|
||||
var ACCESS_ARTICLE_CHANGE = 8; // 01000
|
||||
var ACCESS_ARTICLE_EDIT = 8; // 01000
|
||||
var ACCESS_ARTICLE_VIEW = 16; // 10000
|
||||
</script>
|
||||
[/head]
|
Loading…
Add table
Add a link
Reference in a new issue