renovations

This commit is contained in:
Ilya Kantor 2015-01-09 01:39:01 +03:00
parent 87a3588057
commit 223dd884ae
100 changed files with 638 additions and 658 deletions

View file

@ -92,9 +92,9 @@ alert(isAdmin);
## Особенности встроенных функций ## Особенности встроенных функций
Место, где выводится модальное окно с вопросом, и внешний вид окна выбирает браузер. Разработчик не может на это влиять. Конкретное место, где выводится модальное окно с вопросом -- обычно это центр браузера, и внешний вид окна выбирает браузер. Разработчик не может на это влиять.
С одной стороны -- это недостаток, т.к. нельзя вывести окно в своем дизайне. С одной стороны -- это недостаток, так как нельзя вывести окно в своем, особо красивом, дизайне.
С другой стороны, преимущество этих функций по сравнению с другими, более сложными методами взаимодействия, которые мы изучим в дальнейшем -- как раз в том, что они очень просты. С другой стороны, преимущество этих функций по сравнению с другими, более сложными методами взаимодействия, которые мы изучим в дальнейшем -- как раз в том, что они очень просты.

View file

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before After
Before After

View file

@ -24,14 +24,20 @@ if (year != 2011) {
} }
``` ```
**Рекомендуется использовать фигурные скобки всегда, даже когда команда одна.** Это улучшает читаемость кода. **Рекомендуется использовать фигурные скобки всегда, даже когда команда одна.**
Это улучшает читаемость кода.
## Преобразование к логическому типу ## Преобразование к логическому типу
Оператор `if (...)` вычисляет и преобразует выражение в скобках к логическому типу. Оператор `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 ```js
if (1) { // 1 преобразуется к true if (1) { // 1 преобразуется к true
@ -49,7 +55,7 @@ if (1) { // 1 преобразуется к true
} }
``` ```
Вычисление условия в проверке `if (year != 2011)` может быть вынесено в отдельную переменную: Можно и просто передать уже готовое логическое значение, к примеру, заранее вычисленное в переменной:
```js ```js
var cond = (year != 2011); // true/false var cond = (year != 2011); // true/false
@ -65,7 +71,7 @@ if (cond) {
```js ```js
//+ run //+ run
var year = prompt('Введите год ECMA-262 5.1', ''); var year = prompt('Введите год появления стандарта ECMA-262 5.1', '');
if (year == 2011) { if (year == 2011) {
alert('Да вы знаток!'); alert('Да вы знаток!');
@ -121,7 +127,7 @@ alert(access);
условие ? значение1 : значение2 условие ? значение1 : значение2
``` ```
Проверяется условие, затем если оно верно -- возвращается `значение1 `, если неверно -- `значение2`, например: Проверяется условие, затем если оно верно -- возвращается `значение1`, если неверно -- `значение2`, например:
```js ```js
access = (age > 14) ? true : false; access = (age > 14) ? true : false;
@ -141,7 +147,6 @@ access = age > 14 ? true : false;
```js ```js
access = age > 14; access = age > 14;
``` ```
[/smart] [/smart]
[smart header="\"Тернарный оператор\""] [smart header="\"Тернарный оператор\""]
@ -152,39 +157,36 @@ access = age > 14;
## Несколько операторов '?' ## Несколько операторов '?'
Несколько операторов `if..else` можно заменить последовательностью операторов `'?'`. Например: Последовательность операторов `'?'` позволяет вернуть значение в зависимости не от одного условия, а от нескольких.
Например:
```js ```js
//+ run //+ run
var a = prompt('a?', 1); var age = prompt('возраст?', 18);
*!* var message = (age < 3) ? 'Здравствуй, малыш!' :
var res = (a == 1) ? 'значение1' : (age < 18) ? 'Привет!' :
(a == 2) ? 'значение2' : (age < 100) ? 'Здравствуйте!' :
(a > 2) ? 'значение3' : 'Какой необычный возраст!';
'значение4';
*/!*
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 ```js
var res; if (age < 3) {
message = 'Здравствуй, малыш!';
if (a == 1) { } else if (a < 18) {
res = 'значение1'; message = 'Привет!';
} else if (a == 2) { } else if (age < 100) {
res = 'значение2'; message = 'Здравствуйте!';
} else if (a > 2) {
res = 'значение3';
} else { } else {
res = 'значение4'; message = 'Какой необычный возраст!';
} }
``` ```
@ -225,3 +227,7 @@ if (company == 'Netscape') {
*/!* */!*
``` ```
При чтении кода глаз идёт вертикально и конструкции, занимающие несколько строк, с понятной вложенностью, воспринимаются гораздо легче. Возможно, вы и сами почувствуете, пробежавшись глазами, что синтаксис с `if` более прост и очевиден чем с оператором `'?'`.
Смысл оператора `'?'` -- вернуть то или иное значение, в зависимости от условия. Пожалуйста, используйте его по назначению, а для выполнения разных веток кода есть `if`.

View file

@ -1,8 +1,8 @@
# Логические операторы # Логические операторы
В JavaScript поддерживаются операторы `||` (ИЛИ), `&&` (И) и `!` (НЕ). Для операций над логическими значениями в JavaScript есть `||` (ИЛИ), `&&` (И) и `!` (НЕ).
Они называются *"логическими"*, но в JavaScript могут применяться к значениям любого типа и возвращают также значения любого типа. Хоть они и называются *"логическими"*, но в JavaScript могут применяться к значениям любого типа и возвращают также значения любого типа.
[cut] [cut]
## || (ИЛИ) ## || (ИЛИ)
@ -15,7 +15,7 @@ result = a || b;
**Логическое ИЛИ в классическом программировании работает следующим образом: "если *хотя бы один* из аргументов `true`, то возвращает `true`, иначе -- `false`".** **Логическое ИЛИ в классическом программировании работает следующим образом: "если *хотя бы один* из аргументов `true`, то возвращает `true`, иначе -- `false`".**
Получается следующая таблица результатов: Получается следующая "таблица результатов":
```js ```js
//+ run //+ run
@ -25,9 +25,7 @@ alert( true || false); // true
alert( false || false); // false alert( false || false); // false
``` ```
При вычислении ИЛИ в JavaScript можно использовать любые значения. В этом случае они будут интерпретироваться как логические. Если значение не логического типа -- то оно к нему приводится. Например, число `1` будет воспринято как `true`, а `0` -- как `false`:
Например, число `1` будет воспринято как `true`, а `0` -- как `false`:
```js ```js
//+ run //+ run
@ -86,6 +84,7 @@ alert(x); // undefined, x не присвоен
var x; var x;
*!*false*/!* || (x = 1); *!*false*/!* || (x = 1);
alert(x); // 1 alert(x); // 1
``` ```
@ -93,9 +92,9 @@ alert(x); // 1
Итак, как мы видим, оператор ИЛИ вычисляет ровно столько значений, сколько необходимо -- до первого `true`. Итак, как мы видим, оператор ИЛИ вычисляет ровно столько значений, сколько необходимо -- до первого `true`.
**Оператор ИЛИ возвращает то значение, на котором остановились вычисления.** При этом оператор ИЛИ возвращает то значение, на котором остановились вычисления. Причём, не преобразованное к логическому типу.
Примеры: Например:
```js ```js
//+ run //+ run
@ -122,6 +121,15 @@ var result = undef || zero || emptyStr || msg || 0;
alert(result); // выведет "Привет!" - первое значение, которое является true alert(result); // выведет "Привет!" - первое значение, которое является true
``` ```
Если все значения "ложные", то `||` возвратит последнее из них:
```js
//+ run
alert(undefined || '' || false || 0); // 0
```
## && (И) ## && (И)
@ -131,7 +139,7 @@ alert(result); // выведет "Привет!" - первое значение
result = a && b; result = a && b;
``` ```
**В классическом программировании И возвращает `true`, если оба аргумента истинны, а иначе -- `false`** В классическом программировании И возвращает `true`, если оба аргумента истинны, а иначе -- `false`:
```js ```js
//+ run //+ run
@ -141,7 +149,7 @@ alert( true && false); // false
alert( false && false); // false alert( false && false); // false
``` ```
Пример: Пример c `if`:
```js ```js
//+ run //+ run
@ -152,7 +160,7 @@ if (hour == 12 && minute == 30) {
} }
``` ```
Как и в ИЛИ, допустимы любые значения: Как и в ИЛИ, в И допустимы любые значения:
```js ```js
//+ run //+ run
@ -163,7 +171,7 @@ if ( 1 && 0 ) { // вычислится как true && false
К И применим тот же принцип "короткого цикла вычислений", но немного по-другому, чем к ИЛИ. К И применим тот же принцип "короткого цикла вычислений", но немного по-другому, чем к ИЛИ.
**Если левый аргумент -- `false`, оператор И возвращает его и заканчивает вычисления, а иначе -- вычисляет и возвращает правый аргумент.** **Если левый аргумент -- `false`, оператор И возвращает его и заканчивает вычисления. Иначе -- вычисляет и возвращает правый аргумент.**
Например: Например:
@ -180,6 +188,15 @@ alert(null && 5); // null
alert(0 && "не важно"); // 0 alert(0 && "не важно"); // 0
``` ```
Можно передать и несколько значений подряд, при этом возвратится первое "ложное" (на котором остановились вычисления), а если его нет -- то последнее:
```js
//+ run
alert(1 && 2 && null && 3); // null
alert(1 && 2 && 3); // 3
```
**Приоритет оператора И `&&` больше, чем ИЛИ `||`, т.е. он выполняется раньше.** **Приоритет оператора И `&&` больше, чем ИЛИ `||`, т.е. он выполняется раньше.**
Поэтому в следующем коде сначала будет вычислено правое И: `1 && 0 = 0`, а уже потом -- ИЛИ. Поэтому в следующем коде сначала будет вычислено правое И: `1 && 0 = 0`, а уже потом -- ИЛИ.
@ -189,6 +206,7 @@ alert(0 && "не важно"); // 0
alert(5 || 1 && 0); // 5 alert(5 || 1 && 0); // 5
``` ```
[warn header="Не используйте `&&` вместо `if`"] [warn header="Не используйте `&&` вместо `if`"]
Оператор `&&` в простых случаях можно использовать вместо `if`, например: Оператор `&&` в простых случаях можно использовать вместо `if`, например:
@ -200,7 +218,7 @@ var x = 1;
(x > 0) && alert('Больше'); (x > 0) && alert('Больше');
``` ```
Действие в правой части `&&` выполнится только в том случае, если до него дойдут вычисления. То есть, если в левой части будет `true`. Действие в правой части `&&` выполнится только в том случае, если до него дойдут вычисления. То есть, `alert` сработает, если в левой части будет `true`.
Получился аналог: Получился аналог:
@ -213,7 +231,7 @@ if (x > 0) {
} }
``` ```
Однако, как правило, `if` лучше читается и воспринимается. Он более очевиден, поэтому лучше использовать его. Это, впрочем, относится и к другим неочевидным применениям возможностей языка. Однако, как правило, вариант с `if` лучше читается и воспринимается. Он более очевиден, поэтому лучше использовать его. Это, впрочем, относится и к другим неочевидным применениям возможностей языка.
[/warn] [/warn]
## ! (НЕ) ## ! (НЕ)

View file

@ -9,7 +9,7 @@
<li>Преобразование к логическому значению.</li> <li>Преобразование к логическому значению.</li>
</ol> </ol>
**Эта глава описывает преобразование только примитивных значений, объекты разбираются далее в учебнике.** **Эта глава описывает преобразование только примитивных значений, объекты разбираются далее.**
## Строковое преобразование ## Строковое преобразование
@ -30,6 +30,8 @@ alert(a); // "true"
alert( String(null) === "null" ); // true alert( String(null) === "null" ); // true
``` ```
Как видно из примеров выше, преобразование происходит наиболее очевидным способом, "как есть": `false` становится `"false"`, `null` -- `"null"`, `undefined` -- `"undefined"` и т.п.
Также для явного преобразования применяется оператор `"+"`, у которого один из аргументов строка. В этом случае он приводит к строке и другой аргумент, например: Также для явного преобразования применяется оператор `"+"`, у которого один из аргументов строка. В этом случае он приводит к строке и другой аргумент, например:
```js ```js
@ -42,7 +44,7 @@ alert( "123" + undefined); // "123undefined"
Численное преобразование происходит в математических функциях и выражениях, а также при сравнении данных различных типов (кроме сравнений `===`, `!==`). Численное преобразование происходит в математических функциях и выражениях, а также при сравнении данных различных типов (кроме сравнений `===`, `!==`).
Для преобразования к числу в явном виде можно вызвать `Number(val)`, либо, что короче, поставить перед выражением оператор унарный плюс `"+"`: Для преобразования к числу в явном виде можно вызвать `Number(val)`, либо, что короче, поставить перед выражением унарный плюс `"+"`:
```js ```js
var a = +"123"; // 123 var a = +"123"; // 123
@ -54,14 +56,15 @@ var a = Number("123"); // 123, тот же эффект
<tr><td>`undefined`</td><td>`NaN`</td></tr> <tr><td>`undefined`</td><td>`NaN`</td></tr>
<tr><td>`null`</td><td>`0`</td></tr> <tr><td>`null`</td><td>`0`</td></tr>
<tr><td>`true / false`</td><td>`1 / 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> </table>
Например: Например:
```js ```js
//+ run //+ run
alert( +" \n 123 \n \n"); // 123 // после обрезания пробельных символов останется "123"
alert( +" \n 123 \n \n"); // 123
``` ```
Ещё примеры: Ещё примеры:
@ -79,13 +82,12 @@ alert( +false); // 0
```js ```js
//+ run //+ run
alert( "\n0\n" == 0 ); // true alert( "\n0 " == 0 ); // true
``` ```
При этом строка `"\n0\n"` преобразуется к числу -- начальные и конечные пробелы игнорируются, получается `0`.</li> При этом строка `"\n"` преобразуется к числу, как указано выше: начальные и конечные пробелы обрезаются, получается строка `"0"`, которая равна `0`.</li>
</li> </li>
<li> <li>С логическими значениями:
Ещё пример сравнения разных типов:
```js ```js
//+ run //+ run
@ -118,18 +120,18 @@ alert( "1" == true );
```js ```js
//+ run //+ run
alert(null >= 0); // true, т.к. null преобразуется к 0 alert(null >= 0); // true, т.к. null преобразуется к 0
alert(null > 0); // false (не больше), т.к. null преобразуется к 0 alert(null > 0); // false (не больше), т.к. null преобразуется к 0
alert(null == 0 ); // false (и не равен!), т.к. == рассматривает null особо. alert(null == 0 ); // false (и не равен!), т.к. == рассматривает null особо.
``` ```
Значение `undefined` вообще вне сравнений: Значение `undefined` вообще "несравнимо":
```js ```js
//+ run //+ run
alert(undefined > 0); // false, т.к. undefined -> NaN alert(undefined > 0); // false, т.к. undefined -> NaN
alert(undefined == 0); // false, т.к. это undefined (без преобразования) 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`. Все значения, которые интуитивно "пусты", становятся `false`. Их несколько: `0`, пустая строка, `null`, `undefined` и `NaN`.
@ -172,12 +174,11 @@ alert( !!" " ); // любые непустые строки, даже из пр
**Два значения могут быть равны, но одно из них в логическом контексте `true`, другое -- `false`**. **Два значения могут быть равны, но одно из них в логическом контексте `true`, другое -- `false`**.
Например, равенства в следующем примере верны, так как происходит численное преобразование: Например, равенство в следующем примере верно, так как происходит численное преобразование:
```js ```js
//+ run //+ run
alert( 0 == "\n0\n" ); // true alert( 0 == "\n0\n" ); // true
alert( false == " " ); // true
``` ```
...А в логическом контексте левая часть даст `false`, правая -- `true`: ...А в логическом контексте левая часть даст `false`, правая -- `true`:
@ -196,15 +197,12 @@ if ("\n0\n") {
В JavaScript есть три преобразования: В JavaScript есть три преобразования:
<ol> <ol>
<li>Строковое: `String(value)` -- в строковом контексте или при сложении со строкой</li> <li>Строковое: `String(value)` -- в строковом контексте или при сложении со строкой. Работает очевидным образом.</li>
<li>Численное: `Number(value)` -- в численном контексте, включая унарный плюс `+value`.</li> <li>Численное: `Number(value)` -- в численном контексте, включая унарный плюс `+value`. Происходит при сравнении разных типов, кроме строгого равенства.</li>
<li>Логическое: `Boolean(value)` -- в логическом контексте, можно также сделать двойным НЕ: `!!value`.</li> <li>Логическое: `Boolean(value)` -- в логическом контексте, можно также сделать двойным НЕ: `!!value`.</li>
</ol> </ol>
**Сравнение не осуществляет преобразование типов в следующих случаях:** Точные таблицы преобразований даны выше в этой главе.
<ul>
<li>При сравнении объектов. Две переменные, которые являются объектами равны только, когда ссылаются на один и тот же объект.</li> Особым случаем является проверка равенства с `null` и `undefined`. Они равны друг другу, но не равны чему бы то ни было ещё, этот случай прописан особо в спецификации.
<li>При сравнении двух строк. Там отдельный алгоритм сравнения. А вот если хоть один операнд -- не строка, то значения будут приведены: `true > "000"` станет `1 > 0`.</li>
<li>При проверке равенства с `null` и `undefined`. Они равны друг другу, но не равны чему бы то ни было ещё, этот случай прописан особо в спецификации.</li>
</ul>

View file

@ -6,6 +6,6 @@
Цикл должен спрашивать число пока либо посетитель не введет число, большее `100`, либо не нажмет кнопку Cancel (ESC). Цикл должен спрашивать число пока либо посетитель не введет число, большее `100`, либо не нажмет кнопку Cancel (ESC).
Предполагается, что посетитель вводит только числа. Предполагается, что посетитель вводит только числа, предусматривать обработку нечисловых строк в этой задаче необязательно.
[demo /] [demo /]

View file

@ -1,6 +1,6 @@
# Вывести простые числа # Вывести простые числа
[importance 4] [importance 3]
Натуральное число, большее 1, называется *простым*, если оно ни на что не делится, кроме себя и `1`. Натуральное число, большее 1, называется *простым*, если оно ни на что не делится, кроме себя и `1`.

View 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 нет такой возможности.

View file

@ -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]

View file

@ -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]

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

View file

@ -33,6 +33,8 @@ alert( "нечисло" * 2 ); // NaN, ошибка
Эти значения формально принадлежат типу "число", хотя, конечно, числами в их обычном понимании не являются. Эти значения формально принадлежат типу "число", хотя, конечно, числами в их обычном понимании не являются.
Особенности работы с числами в JavaScript разобраны в главе [](/number).
## Строка "string" ## Строка "string"
```js ```js
@ -46,6 +48,8 @@ str = 'Одинарные кавычки тоже подойдут';
В некоторых языках программирования есть специальный тип данных для одного символа. Например, в языке С это `char`. В JavaScript есть только тип "строка" `string`. Что, надо сказать, вполне удобно. В некоторых языках программирования есть специальный тип данных для одного символа. Например, в языке С это `char`. В JavaScript есть только тип "строка" `string`. Что, надо сказать, вполне удобно.
[/smart] [/smart]
Более подробно со строками мы познакомимся в главе [](/string).
## Булевый (логический) тип "boolean" ## Булевый (логический) тип "boolean"
У него всего два значения: `true` (истина) и `false` (ложь). У него всего два значения: `true` (истина) и `false` (ложь).
@ -101,7 +105,7 @@ alert(x); // "undefined"
Особняком стоит шестой тип: *"объекты"*. К нему относятся, например, даты, функции, он используется для коллекций данных и для объявления более сложных сущностей. Особняком стоит шестой тип: *"объекты"*. К нему относятся, например, даты, функции, он используется для коллекций данных и для объявления более сложных сущностей.
Позже мы вернёмся к этому типу и рассмотрим его принципиальные отличия от примитивов. Позже, в главе [про объекты](/object) мы вернёмся к этому типу и рассмотрим его принципиальные отличия от примитивов.
## Итого ## Итого

View file

@ -1,11 +1,18 @@
# Основные операторы # Основные операторы
Для работы с переменными, со значениями, JavaScript поддерживает все стандартные операторы, большинство которых есть и в других языках программирования. Для работы с переменными, со значениями, JavaScript поддерживает все стандартные операторы, большинство которых есть и в других языках программирования.
Несколько операторов мы знаем со школы -- это обычные сложение `+`, умножение `*`, вычитание и так далее.
В этой главе мы сконцентрируемся на операторах, которые в на математике не проходят и на их особенностях именно в JavaScript.
[cut] [cut]
## Термины: "унарный", "бинарный", "операнд" ## Термины: "унарный", "бинарный", "операнд"
У операторов есть своя терминология, которая используется во всех языках программирования. У операторов есть своя терминология, которая используется во всех языках программирования.
Прежде, чем мы двинемся дальше -- несколько терминов, чтобы понимать, о чём речь.
<ul> <ul>
<li>*Операнд* -- то, к чему применяется оператор. Например: `5 * 2` -- оператор умножения с левым и правым операндами. Другое название: "аргумент оператора".</li> <li>*Операнд* -- то, к чему применяется оператор. Например: `5 * 2` -- оператор умножения с левым и правым операндами. Другое название: "аргумент оператора".</li>
<li>*Унарным* называется оператор, который применяется к одному выражению. Например, оператор унарный минус `"-"` меняет знак числа на противоположный: <li>*Унарным* называется оператор, который применяется к одному выражению. Например, оператор унарный минус `"-"` меняет знак числа на противоположный:
@ -13,9 +20,11 @@
```js ```js
//+ run //+ run
var x = 1; var x = 1;
alert( -x ); // -1, унарный минус
alert( -(x+2) ); // -3, унарный минус применён к результату сложения x+2 *!*
alert( -(-3) ); // 3 x = -x;
*/!*
alert( x ); // -1, применили унарный минус
``` ```
</li> </li>
@ -26,54 +35,23 @@ alert( -(-3) ); // 3
var x = 1, y = 3; var x = 1, y = 3;
alert( y - x ); // 2, бинарный минус alert( y - x ); // 2, бинарный минус
``` ```
</li> </li>
</ul> </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 ```js
var a = "моя" + "строка"; var a = "моя" + "строка";
alert(a); // моястрока alert(a); // моястрока
``` ```
Иначе говорят, что "плюс производит конкатенацию (сложение) строк".
**Если хотя бы один аргумент является строкой, то второй будет также преобразован к строке!** **Если хотя бы один аргумент является строкой, то второй будет также преобразован к строке!**
Причем не важно, справа или слева находится операнд-строка, в любом случае нестроковый аргумент будет преобразован. Например: Причем не важно, справа или слева находится операнд-строка, в любом случае нестроковый аргумент будет преобразован. Например:
@ -84,12 +62,6 @@ alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21" alert( 2 + '1' ); // "21"
``` ```
[smart]
Для сложения строк в программировании используется специальный термин "конкатенация" (от англ. concat).
В примерах выше оператор `'+'` производил конкатенацию строк.
[/smart]
**Это приведение к строке -- особенность исключительно бинарного оператора `"+"`.** **Это приведение к строке -- особенность исключительно бинарного оператора `"+"`.**
Остальные арифметические операторы работают только с числами и всегда приводят аргументы к числу. Остальные арифметические операторы работают только с числами и всегда приводят аргументы к числу.
@ -98,18 +70,20 @@ alert( 2 + '1' ); // "21"
```js ```js
//+ run //+ run
alert( '1' - 2 ); // -1 alert( 2 - '1' ); // 1
alert( 6 / '2'); // 3 alert( 6 / '2'); // 3
``` ```
### Унарный плюс +
Унарный, то есть применённый к одному значению, плюс как арифметический оператор ничего не делает:
### Преобразование к числу, унарный плюс +
Унарный, то есть применённый к одному значению, плюс ничего не делает с числами:
```js ```js
//+ run //+ run
alert( +1 ); // 1 alert( +1 ); // 1
alert( +(1-2) ); // -1 alert( +(1 - 2) ); // -1
``` ```
Как видно, плюс ничего не изменил в выражениях. Результат -- такой же, как и без него. Как видно, плюс ничего не изменил в выражениях. Результат -- такой же, как и без него.
@ -118,52 +92,69 @@ alert( +(1-2) ); // -1
Например, когда мы получаем значения из HTML-полей или от пользователя, то они обычно в форме строк. Например, когда мы получаем значения из HTML-полей или от пользователя, то они обычно в форме строк.
А что, если их нужно сложить? Бинарный плюс сложит их как строки: А что, если их нужно, к примеру, сложить? Бинарный плюс сложит их как строки:
```js ```js
//+ run //+ run
var a = "2"; var apples = "2";
var b = "3"; var oranges = "3";
alert( a + b ); // "23", так как бинарный плюс складывает строки alert( apples + oranges ); // "23", так как бинарный плюс складывает строки
``` ```
Поэтому используем унарный плюс, чтобы преобразовать к числу: Поэтому используем унарный плюс, чтобы преобразовать к числу:
```js ```js
//+ run //+ run
var a = "2"; var apples = "2";
var b = "3"; 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 ```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 ```js
@ -182,7 +173,9 @@ alert(c); // 4
Такое присваивание работает справа-налево, то есть сначала вычислятся самое правое выражение `2+2`, присвоится в `c`, затем выполнится `b = c` и, наконец, `a = b`. Такое присваивание работает справа-налево, то есть сначала вычислятся самое правое выражение `2+2`, присвоится в `c`, затем выполнится `b = c` и, наконец, `a = b`.
[smart header="Оператор `\"=\"` возвращает значение"] [smart header="Оператор `\"=\"` возвращает значение"]
Все операторы возвращают значение. Вызов `x = выражение` записывает выражение в `x`, а затем возвращает его. Благодаря этому присваивание можно использовать как часть более сложного выражения: Все операторы возвращают значение. Вызов `x = выражение` не является исключением.
Он записывает выражение в `x`, а затем возвращает его. Благодаря этому присваивание можно использовать как часть более сложного выражения:
```js ```js
//+ run //+ run
@ -205,29 +198,21 @@ alert(c); // 0
[/smart] [/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> </li>
</ul> </ul>
**Инкремент/декремент можно использовать в любых выражениях.** [smart header="Инкремент/декремент можно использовать в любых выражениях"]
При этом он имеет более высокий приоритет и выполняется раньше, чем арифметические операции: При этом он имеет более высокий приоритет и выполняется раньше, чем арифметические операции:
@ -352,6 +337,7 @@ var i = 1;
alert( 2 * i ); alert( 2 * i );
i++; i++;
``` ```
[/smart]
## Побитовые операторы ## Побитовые операторы
@ -370,11 +356,11 @@ i++;
<li>ZERO-FILL RIGHT SHIFT(правый сдвиг с заполнением нулями) ( `>>>` )</li> <li>ZERO-FILL RIGHT SHIFT(правый сдвиг с заполнением нулями) ( `>>>` )</li>
</ul> </ul>
Вы можете более подробно почитать о них в отдельной статье [](/bitwise-operators). Они используются редко, поэтому вынесены в отдельную главу [](/bitwise-operators).
## Вызов операторов с присваиванием ## Сокращённая арифметика с присваиванием
Часто нужно применить оператор к переменной и сохранить результат в ней же, например: Часто нужно применить оператор к переменной и сохранить результат в ней же, например:
@ -384,9 +370,7 @@ n = n + 5;
n = n * 2; n = n * 2;
``` ```
Эту запись можно укоротить при помощи совмещённых операторов: <code>+=, -=, *=, /=, &gt;&gt;=, &lt;&lt;=, &gt;&gt;&gt;=, &=, |=, ^=</code>. Эту запись можно укоротить при помощи совмещённых операторов, вот так:
Вот так:
```js ```js
//+ run //+ run
@ -397,13 +381,24 @@ n *= 2; // теперь n=14 (работает как n = n * 2)
alert(n); // 14 alert(n); // 14
``` ```
Все эти операторы имеют в точности такой же приоритет, как обычное присваивание, то есть выполняются после большинства других операций. Так можно сделать для операторов `+,-,*,/` и бинарных `<<,>>,>>>,&,|,^`.
Вызов с присваиванием имеет в точности такой же приоритет, как обычное присваивание, то есть выполнится после большинства других операций:
```js
//+ run
var n = 2;
n *= 3 + 5;
alert(n); // 16 (n = 2 * 8)
```
## Оператор запятая ## Оператор запятая
Запятая тоже является оператором. Ее можно вызвать явным образом, например: Один из самых необычных операторов -- запятая `','`.
Его можно вызвать явным образом, например:
```js ```js
//+ run //+ run

View file

@ -1,20 +1,22 @@
# Операторы сравнения и логические значения # Операторы сравнения и логические значения
В этом разделе мы познакомимся с операторами сравнения и с логическими значениями, которые такие операторы возвращают. В этом разделе мы познакомимся с операторами сравнения и с логическими значениями, которые такие операторы возвращают.
[cut] [cut]
Многие операторы сравнения знакомы нам со школы:
Многие операторы сравнения знакомы нам из математики:
<ul> <ul>
<li>Больше/меньше: <code>a &gt; b</code>, <code>a &lt; b</code>.</li> <li>Больше/меньше: <code>a &gt; b</code>, <code>a &lt; b</code>.</li>
<li>Больше/меньше или равно: <code>a &gt;= b</code>, <code>a &lt;= b</code>.</li> <li>Больше/меньше или равно: <code>a &gt;= b</code>, <code>a &lt;= b</code>.</li>
<li>Равно `a == b`. <li>Равно `a == b`.
Для сравнения используется два символа равенства `'='`. Один символ `a = b` означал бы присваивание.</li> Для сравнения используется два символа равенства `'='`. Один символ `a = b` означал бы присваивание.</li>
<li>"Не равно". В школе он пишется как <code>&ne;</code>, в JavaScript -- знак равенства с восклицательным знаком перед ним <code>!=</code>.</li> <li>"Не равно". В математике он пишется как <code>&ne;</code>, в JavaScript -- знак равенства с восклицательным знаком перед ним <code>!=</code>.</li>
</ul> </ul>
## Логические значения ## Логические значения
Как и другие операторы, сравнение возвращает значение. Это значение имеет специальный *логический* тип. Как и другие операторы, сравнение возвращает значение. Это значение имеет *логический* тип.
Существует всего два логических значения: Существует всего два логических значения:
<ul> <ul>
@ -35,12 +37,12 @@ alert( 2 != 1 ); // true
```js ```js
//+ run //+ run
var a = true; // присвоили явно var a = true; // присваивать явно
var b = 3 > 4; // false
var b = 3 > 4; // или как результат сравнения
alert( b ); // false 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 ```js
//+ run //+ run
alert(0 == false); // true, так как false преобразуется к 0 alert(0 == false); // true
``` ```
Та же ситуация с пустой строкой:
```js
//+ run
alert('' == false); // true
```
Это естественное следствие того, что операнды разных типов преобразовались к числу. Пустая строка, как и `false`, при преобразовании к числу дают `0`.
Что же делать, если всё же нужно отличить `0` от `false`? Что же делать, если всё же нужно отличить `0` от `false`?
**Для проверки равенства без преобразования типов используются операторы строгого равенства `===` (тройное равно) и `!==`.** **Для проверки равенства без преобразования типов используются операторы строгого равенства `===` (тройное равно) и `!==`.**
Они сравнивают без приведения типов. Если тип разный, то такие значения всегда неравны (строго): Если тип разный, то они всегда возвращают `false`:
```js ```js
//+ run //+ run
@ -175,7 +187,7 @@ alert(0 === false); // false, т.к. типы различны
Посмотрим забавные следствия. Посмотрим забавные следствия.
[smart header="Некорректный результат сравнения `null` с `0`"] ### Некорректный результат сравнения null с 0
Сравним `null` с нулём: Сравним `null` с нулём:
```js ```js
@ -199,9 +211,8 @@ alert(null >= 0); // *!*true*/!*
В результате получается странная с точки зрения здравого смысла ситуация, которую мы видели в примере выше. В результате получается странная с точки зрения здравого смысла ситуация, которую мы видели в примере выше.
[/smart] ### Несравнимый undefined
[smart header="Несравнимый `undefined`"]
Значение `undefined` вообще нельзя сравнивать: Значение `undefined` вообще нельзя сравнивать:
```js ```js
@ -215,7 +226,7 @@ alert(undefined == 0); // false (3)
<li>Сравнения `(1)` и `(2)` дают `false` потому, что `undefined` при преобразовании к числу даёт `NaN`. А значение `NaN` по стандарту устроено так, что сравнения `==`, `<`, `>`, `<=`, `>=` и даже `===` с ним возвращают `false`.</li> <li>Сравнения `(1)` и `(2)` дают `false` потому, что `undefined` при преобразовании к числу даёт `NaN`. А значение `NaN` по стандарту устроено так, что сравнения `==`, `<`, `>`, `<=`, `>=` и даже `===` с ним возвращают `false`.</li>
<li>Проверка равенства `(3)` даёт `false`, потому что в стандарте явно прописано, что `undefined` равно лишь `null` и ничему другому.</li> <li>Проверка равенства `(3)` даёт `false`, потому что в стандарте явно прописано, что `undefined` равно лишь `null` и ничему другому.</li>
</ul> </ul>
[/smart]
**Вывод: любые сравнения с `undefined/null`, кроме точного `===`, следует делать с осторожностью.** **Вывод: любые сравнения с `undefined/null`, кроме точного `===`, следует делать с осторожностью.**

View file

@ -2,7 +2,7 @@
Побитовые операторы интерпретируют операнды как последовательность из 32 битов (нулей и единиц). Они производят операции, используя двоичное представление числа, и возвращают новую последовательность из 32 бит (число) в качестве результата. Побитовые операторы интерпретируют операнды как последовательность из 32 битов (нулей и единиц). Они производят операции, используя двоичное представление числа, и возвращают новую последовательность из 32 бит (число) в качестве результата.
**Эта глава сложная, требует дополнительных знаний в программировании и не очень важная, вы можете пропустить её.** Эта глава требует дополнительных знаний в программировании и не очень важная, при первом чтении вы можете пропустить её и вернуться потом, когда захотите понять, как побитовые операторы работают.
[cut] [cut]
## Формат 32-битного целого числа со знаком [#signed-format] ## Формат 32-битного целого числа со знаком [#signed-format]
@ -31,11 +31,6 @@ a = 255;// 00000000000000000000000011111111
Обратите внимание, каждое число состоит ровно из 32-битов. Обратите внимание, каждое число состоит ровно из 32-битов.
[smart header="Младший бит слева"]
Несмотря на то, что нам такой способ записи чисел кажется не совсем обычным, бывают языки и технологии, использующие способ записи "младший бит слева", когда биты пишутся наоборот, от меньшего разряда к большему.
Именно поэтому спецификация EcmaScript явно говорит "старший бит слева".
[/smart]
</li> </li>
<li>*Дополнение до двойки* -- это название способа поддержки отрицательных чисел. <li>*Дополнение до двойки* -- это название способа поддержки отрицательных чисел.
@ -117,8 +112,6 @@ a = 255;// 00000000000000000000000011111111
</tr> </tr>
</table> </table>
## Описание работы операторов
Побитовые операторы работают следующим образом: Побитовые операторы работают следующим образом:
<ol> <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) = 8 (по осн. 10)
``` ```
### | (Побитовое ИЛИ) ## | (Побитовое ИЛИ)
Выполняет операцию ИЛИ над каждой парой бит. Результат `a | b` равен 1, если хотя бы один бит из <code>a,b</code> равен 1. Выполняет операцию ИЛИ над каждой парой бит. Результат `a | b` равен 1, если хотя бы один бит из <code>a,b</code> равен 1.
@ -185,7 +204,7 @@ a = 255;// 00000000000000000000000011111111
= 15 (по осн. 10) = 15 (по осн. 10)
``` ```
### ^ (Исключающее ИЛИ) ## ^ (Исключающее ИЛИ)
Выполняет операцию "Исключающее ИЛИ" над каждой парой бит. Выполняет операцию "Исключающее ИЛИ" над каждой парой бит.
@ -271,9 +290,7 @@ a = 255;// 00000000000000000000000011111111
[/smart] [/smart]
## ~ (Побитовое НЕ)
### ~ (Побитовое НЕ)
Производит операцию НЕ над каждым битом, заменяя его на обратный ему. Производит операцию НЕ над каждым битом, заменяя его на обратный ему.
@ -429,14 +446,21 @@ alert( 100 >> 3 ); // 12, деление на 2 три раза, целая ча
### Маска ### Маска
Для этого примера представим, что наш скрипт работает с пользователями: Для этого примера представим, что наш скрипт работает с пользователями.
У них могут быть различные роли в проекте:
<ul> <ul>
<li>`Гость`</li> <li>`Гость`</li>
<li>`Редактор`</li> <li>`Редактор`</li>
<li>`Админ`</li> <li>`Админ`</li>
</ul> </ul>
У каждого из них есть ряд доступов, которые можно свести в таблицу: Каждой роли соответствует ряд доступов к статьям и функционалу сайта.
Например, `Гость` может лишь просматривать статьи сайта, а `Редактор` -- ещё и редактировать их, и тому подобное.
Что-то в таком духе:
<table> <table>
<tr> <tr>
@ -514,7 +538,11 @@ alert( 100 >> 3 ); // 12, деление на 2 три раза, целая ча
</tr> </tr>
</table> </table>
**Мы "упаковали" много информации в одно число. Это экономит память. Но, кроме этого, по нему очень легко проверить, имеет ли посетитель заданную *комбинацию доступов*!** В последней колонке находится десятичное число, которое получится, если прочитать строку доступов в двоичном виде.
Например, доступ гостя `10100 = 20`.
Такая интерпретация доступов позволяет "упаковать" много информации в одно число. Это экономит память, а кроме этого -- это удобно, поскольку в дополнение к экономии -- по такому значению очень легко проверить, имеет ли посетитель заданную *комбинацию доступов*!
Для этого посмотрим, как в 2-ной системе представляется каждый доступ в отдельности. Для этого посмотрим, как в 2-ной системе представляется каждый доступ в отдельности.
@ -526,113 +554,63 @@ alert( 100 >> 3 ); // 12, деление на 2 три раза, целая ча
<li>Доступ, соответствующий только просмотру статей: `10000 (=16)`.</li> <li>Доступ, соответствующий только просмотру статей: `10000 (=16)`.</li>
</ul> </ul>
Например, просматривать и изменять статьи позволит доступ `access = 11000`. Доступ одновременно на просмотр и изменение статей -- это двоичное число с `1` на соответствующих позициях, то есть `access = 11000`.
Как правило, доступы задаются в виде констант: Как правило, доступы задаются в виде констант:
```js ```js
var ACCESS_ADMIN = 1; // 00001 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_GOODS_VIEW = 4; // 00100
var ACCESS_ARTICLE_CHANGE = 8; // 01000 var ACCESS_ARTICLE_EDIT = 8; // 01000
var ACCESS_ARTICLE_VIEW = 16; // 10000 var ACCESS_ARTICLE_VIEW = 16; // 10000
``` ```
Из этих констант получить нужную комбинацию доступов можно при помощи операции `|`. Из этих констант получить нужную комбинацию доступов можно при помощи операции `|`.
```js ```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
``` ```
alert(editor & ACCESS_ADMIN); // 0, доступа нет
### Проверка доступов alert(editor & ACCESS_ARTICLE_EDIT); // 8, доступ есть
Для того, чтобы понять, есть ли в доступе `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, нет доступа к управлению правами
``` ```
Такая проверка работает, потому что оператор И ставит `1` на те позиции результата, на которых в обоих операндах стоит `1`. Такая проверка работает, потому что оператор И ставит `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-ной системе). Например, проверим, есть ли права на просмотр ИЛИ изменение товаров. Соответствующие права задаются битом `1` на втором и третьем месте с конца, что даёт число `00110` (=`6` в 10-ной системе).
```js ```js
//+ run var check = ACCESS_GOODS_VIEW | ACCESS_GOODS_EDIT; // 6, 00110
var check = ACCESS_GOODS_VIEW | ACCESS_GOODS_CHANGE; // 6, 00110
var access = 30; // 11*!*11*/!*0; alert(admin & check); // не 0, значит есть доступ к просмотру ИЛИ изменению
alert(access & check); // не 0, значит есть доступ к просмотру ИЛИ изменению
access = parseInt("11100", 2);
alert(access & check); // не 0, есть доступ к просмотру ИЛИ изменению
``` ```
Как видно из примера выше, если в аргументе `check` стоит ИЛИ из нескольких доступов `ACCESS_*`, то и результат проверки скажет, есть ли хотя бы один из них. А какой -- нужно смотреть отдельной проверкой, если это важно. *Битовой маской* называют как раз комбинацию двоичных значений (`check` в примере выше), которая используется для проверки и выборки единиц на нужных позициях.
**Итак, маска даёт возможность удобно "паковать" много битовых значений в одно число при помощи ИЛИ `|`, а также, при помощи оператора И (`&`), проверять маску на комбинацию установленных битов.** Маски могут быть весьма удобны.
### Маски в функциях В частности, их используют в функциях, чтобы одним параметром передать несколько "флагов", т.е. однобитных значений.
Зачастую маски используют в функциях, чтобы одним параметром передать несколько "флагов", т.е. однобитных значений.
Например:
Пример вызова функции с маской:
```js ```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 ### Проверка на -1
[Внутренний формат](#signed-format) чисел устроен так, что для смены знака нужно все биты заменить на противоположные ("обратить") и прибавить `1`. [Внутренний формат](#signed-format) 32-битных чисел устроен так, что для смены знака нужно все биты заменить на противоположные ("обратить") и прибавить `1`.
Обращение битов -- это побитовое НЕ (`~`). То есть, при таком формате представления числа `-n = ~n + 1`. Или, если перенести единицу: `~n = -(n+1)`. Обращение битов -- это побитовое НЕ (`~`). То есть, при таком формате представления числа `-n = ~n + 1`. Или, если перенести единицу: `~n = -(n+1)`.
@ -706,7 +684,7 @@ if (~str.indexOf("верка")) { // Сочетание "if (~...indexOf)" чи
### Умножение и деление на степени 2 ### Умножение и деление на степени 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 бита. При этом следует иметь в виду, что максимальный верхний порог такого умножения меньше, чем обычно, так как побитовый оператор оперирует 32-битными целыми, в то время как обычные операторы оперируют числами длиной 64 бита.
**Оператор `a >> b`, сдвигая биты, производит целочисленное деление `a` на <code>2<sup>b</sup></code>.** Оператор сдвига в другую сторону `a >> b`, производит обратную операцию -- целочисленное деление `a` на <code>2<sup>b</sup></code>.
```js ```js
//+ run //+ run
@ -736,17 +714,18 @@ alert( 11 >> 2 ); // 2, целочисленное деление (менее з
Как правило, битовое представление числа используется для: Как правило, битовое представление числа используется для:
<ul> <ul>
<li>Упаковки нескольких битововых значений ("флагов") в одно значение. Это экономит память и позволяет проверять наличие комбинации флагов одним оператором `&`. Кроме того, такое упакованное значение будет для функции всего одним параметром, это тоже удобно.</li>
<li>Округления числа: `(12.34^0) = 12`.</li> <li>Округления числа: `(12.34^0) = 12`.</li>
<li>Проверки на равенство `-1`: `if (~n) { n не -1 }`.</li> <li>Проверки на равенство `-1`: `if (~n) { n не -1 }`.</li>
<li>Упаковки нескольких битововых значений ("флагов") в одно значение. Это экономит память и позволяет проверять наличие комбинации флагов одним оператором `&`.</li>
<li>Других ситуаций, когда нужны битовые маски.</li>
</ul> </ul>
[head] [head]
<script> <script>
var ACCESS_ADMIN = 1; // 00001 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_GOODS_VIEW = 4; // 00100
var ACCESS_ARTICLE_CHANGE = 8; // 01000 var ACCESS_ARTICLE_EDIT = 8; // 01000
var ACCESS_ARTICLE_VIEW = 16; // 10000 var ACCESS_ARTICLE_VIEW = 16; // 10000
</script> </script>
[/head] [/head]