up
This commit is contained in:
parent
084c2ac6bc
commit
98a5df9c55
12 changed files with 193 additions and 1090 deletions
|
@ -1,6 +0,0 @@
|
|||
<ol>
|
||||
<li>Операция `a^b` ставит бит результата в `1`, если на соответствующей битовой позиции в `a` или `b` (но не одновременно) стоит `1`.
|
||||
|
||||
Так как в `0` везде стоят нули, то биты берутся в точности как во втором аргументе.</li>
|
||||
<li>Первое побитовое НЕ `~` превращает `0` в `1`, а `1` в `0`. А второе НЕ превращает ещё раз, в итоге получается как было.</li>
|
||||
</ol>
|
|
@ -1,13 +0,0 @@
|
|||
# Побитовый оператор и значение
|
||||
|
||||
[importance 5]
|
||||
|
||||
Почему побитовые операции в примерах ниже не меняют число? Что они делают внутри?
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 123 ^ 0 ); // 123
|
||||
alert( 0 ^ 123 ); // 123
|
||||
alert( ~~123 ); // 123
|
||||
```
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
Один из вариантов такой функции:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function isInteger(num) {
|
||||
return (num ^ 0) === num;
|
||||
}
|
||||
|
||||
alert( isInteger(1) ); // true
|
||||
alert( isInteger(1.5) ); // false
|
||||
alert( isInteger(-0.5) ); // false
|
||||
```
|
||||
|
||||
Обратите внимание: `num^0` -- в скобках! Это потому, что приоритет операции `^` очень низкий. Если не поставить скобку, то `===` сработает раньше. Получится `num ^ (0 === num)`, а это уже совсем другое дело.
|
|
@ -1,14 +0,0 @@
|
|||
# Проверка, целое ли число
|
||||
|
||||
[importance 3]
|
||||
|
||||
Напишите функцию `isInteger(num)`, которая возвращает `true`, если `num` -- целое число, иначе `false`.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
alert( isInteger(1) ); // true
|
||||
alert( isInteger(1.5) ); // false
|
||||
alert( isInteger(-0.5) ); // false
|
||||
```
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
Операция над числами, в конечном итоге, сводится к битам.
|
||||
|
||||
Посмотрим, можно ли поменять местами биты слева и справа.
|
||||
|
||||
Например, таблица истинности для `^`:
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>`a`</th>
|
||||
<th>`b`</th>
|
||||
<th>результат</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
|
||||
<tr><td>`0`</td><td>`1`</td><td>`1`</td></tr>
|
||||
<tr><td>`1`</td><td>`0`</td><td>`1`</td></tr>
|
||||
<tr><td>`1`</td><td>`1`</td><td>`0`</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Случаи `0^0` и `1^1` заведомо не изменятся при перемене мест, поэтому нас не интересуют. А вот `0^1` и `1^0` эквивалентны и равны `1`.
|
||||
|
||||
Аналогично можно увидеть, что и другие операторы симметричны.
|
||||
|
||||
Ответ: **да**.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# Симметричны ли операции ^, |, &?
|
||||
|
||||
[importance 5]
|
||||
|
||||
Верно ли, что для любых `a` и `b` выполняются равенства ниже?
|
||||
<ul>
|
||||
<li>`a ^ b == b ^ a`</li>
|
||||
<li>`a & b == b & a`</li>
|
||||
<li>`a | b == b | a`</li>
|
||||
</ul>
|
||||
|
||||
Иными словами, при перемене мест -- всегда ли результат остаётся тем же?
|
|
@ -1,29 +0,0 @@
|
|||
Всё дело в том, что побитовые операции преобразуют число в 32-битное целое.
|
||||
|
||||
Обычно число в JavaScript имеет 64-битный формат с плавающей точкой. При этом часть битов (`52`) отведены под цифры, часть (`11`) отведены под хранение номера позиции, на которой стоит десятичная точка, и один бит -- знак числа.
|
||||
|
||||
Это означает, что максимальное целое число, которое можно хранить, занимает `52` бита.
|
||||
|
||||
Число `12345678912345` в двоичном виде: `10110011101001110011110011100101101101011001` (44 цифры).
|
||||
|
||||
Побитовый оператор `^` преобразует его в 32-битное путём отбрасывания десятичной точки и "лишних" старших цифр. При этом, так как число большое и старшие биты здесь ненулевые, то, естественно, оно изменится.
|
||||
|
||||
Вот ещё пример:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
// в двоичном виде 1000000000000000000000000000000 (31 цифры)
|
||||
alert( Math.pow(2, 30) ); // 1073741824
|
||||
alert( Math.pow(2, 30) ^ 0 ); // 1073741824, всё ок, длины хватает
|
||||
|
||||
// в двоичном виде 100000000000000000000000000000000 (33 цифры)
|
||||
alert( Math.pow(2, 32) ); // 4294967296
|
||||
alert( Math.pow(2, 32) ^ 0 ); // 0, отброшены старшие цифры, остались нули
|
||||
|
||||
// пограничный случай
|
||||
// в двоичном виде 10000000000000000000000000000000 (32 цифры)
|
||||
alert( Math.pow(2, 31) ); // 2147483648
|
||||
alert( Math.pow(2, 31) ^ 0 ); // -2147483648, ничего не отброшено,
|
||||
// но первый бит 1 теперь стоит в начале числа и является знаковым
|
||||
```
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# Почему результат разный?
|
||||
|
||||
[importance 5]
|
||||
|
||||
Почему результат второго `alert'а` такой странный?
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 123456789 ^ 0 ); // 123456789
|
||||
alert( 12345678912345 ^ 0 ); // 1942903641
|
||||
```
|
||||
|
|
@ -1,751 +0,0 @@
|
|||
# Побитовые операторы
|
||||
|
||||
Побитовые операторы интерпретируют операнды как последовательность из 32 битов (нулей и единиц). Они производят операции, используя двоичное представление числа, и возвращают новую последовательность из 32 бит (число) в качестве результата.
|
||||
|
||||
Эта глава требует дополнительных знаний в программировании и не очень важная, при первом чтении вы можете пропустить её и вернуться потом, когда захотите понять, как побитовые операторы работают.
|
||||
[cut]
|
||||
|
||||
## Формат 32-битного целого числа со знаком [#signed-format]
|
||||
|
||||
Побитовые операторы в JavaScript работают с 32-битными целыми числами в их двоичном представлении.
|
||||
|
||||
Это представление называется "32-битное целое со знаком, старшим битом слева и дополнением до двойки".
|
||||
|
||||
Разберём, как устроены числа внутри подробнее, это необходимо знать для битовых операций с ними.
|
||||
|
||||
<ul>
|
||||
<li>Что такое [двоичная система счисления](http://ru.wikipedia.org/wiki/%C4%E2%EE%E8%F7%ED%E0%FF_%F1%E8%F1%F2%E5%EC%E0_%F1%F7%E8%F1%EB%E5%ED%E8%FF), вам, надеюсь, уже известно. При разборе побитовых операций мы будем обсуждать именно двоичное представление чисел, из 32 бит.
|
||||
|
||||
</li>
|
||||
<li>*Старший бит слева* -- это научное название для самого обычного порядка записи цифр (от большего разряда к меньшему). При этом, если больший разряд отсутствует, то соответствующий бит равен нулю.
|
||||
|
||||
Примеры представления чисел в двоичной системе:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
a = 0; // 00000000000000000000000000000000
|
||||
a = 1; // 00000000000000000000000000000001
|
||||
a = 2; // 00000000000000000000000000000010
|
||||
a = 3; // 00000000000000000000000000000011
|
||||
a = 255;// 00000000000000000000000011111111
|
||||
```
|
||||
|
||||
Обратите внимание, каждое число состоит ровно из 32-битов.
|
||||
|
||||
|
||||
</li>
|
||||
<li>*Дополнение до двойки* -- это название способа поддержки отрицательных чисел.
|
||||
|
||||
**Двоичный вид числа, обратного данному (например, `5` и `-5`) получается путём обращения всех битов с прибавлением 1.**
|
||||
|
||||
То есть, нули заменяются на единицы, единицы -- на нули и к числу прибавляется `1`. Получается внутреннее представление того же числа, но со знаком минус.
|
||||
|
||||
Например, вот число `314`:
|
||||
|
||||
```js
|
||||
00000000000000000000000100111010
|
||||
```
|
||||
|
||||
Чтобы получить `-314`, первый шаг -- обратить биты числа: заменить `0` на `1`, а `1` на `0`:
|
||||
|
||||
```js
|
||||
11111111111111111111111011000101
|
||||
```
|
||||
|
||||
Второй шаг -- к полученному двоичному числу приплюсовать единицу, обычным двоичным сложением: `11111111111111111111111011000101 + 1 = 11111111111111111111111011000110`.
|
||||
|
||||
Итак, мы получили:
|
||||
|
||||
```js
|
||||
-314 = 11111111111111111111111011000110
|
||||
```
|
||||
|
||||
Принцип дополнения до двойки делит все двоичные представления на два множества: если крайний-левый бит равен `0` -- число положительное, если `1` -- число отрицательное. Поэтому этот бит называется <i>знаковым битом</i>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
## Список операторов
|
||||
|
||||
В следующей таблице перечислены все побитовые операторы.
|
||||
Далее операторы разобраны более подробно.
|
||||
|
||||
<table class="fullwidth-table">
|
||||
<tr>
|
||||
<th>Оператор</th>
|
||||
<th>Использование</th>
|
||||
<th>Описание</th>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Побитовое И (AND)</td>
|
||||
<td style="white-space: nowrap"><code>a & b</code></td>
|
||||
<td>Ставит 1 на бит результата, для которого соответствующие биты операндов равны 1.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Побитовое ИЛИ (OR)</td>
|
||||
<td style="white-space: nowrap"><code>a | b</code></td>
|
||||
<td>Ставит 1 на бит результата, для которого хотя бы один из соответствующих битов операндов равен 1.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Побитовое исключающее ИЛИ (XOR)</td>
|
||||
<td style="white-space: nowrap"><code>a ^ b</code></td>
|
||||
<td>Ставит 1 на бит результата, для которого только один из соответствующих битов операндов равен 1 (но не оба).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Побитовое НЕ (NOT)</td>
|
||||
<td style="white-space: nowrap"><code>~a</code></td>
|
||||
<td>Заменяет каждый бит операнда на противоположный.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Левый сдвиг</td>
|
||||
<td style="white-space: nowrap">`a << b`</td>
|
||||
<td>Сдвигает двоичное представление <code>a</code> на <code>b</code> битов влево, добавляя справа нули.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Правый сдвиг, переносящий знак</td>
|
||||
<td style="white-space: nowrap">`a >> b`</td>
|
||||
<td>Сдвигает двоичное представление <code>a</code> на <code>b</code> битов вправо, отбрасывая сдвигаемые биты.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Правый сдвиг с заполнением нулями</td>
|
||||
<td style="white-space: nowrap">`a >>> b`</td>
|
||||
<td>Сдвигает двоичное представление <code>a</code> на <code>b</code> битов вправо, отбрасывая сдвигаемые биты и добавляя нули слева.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Побитовые операторы работают следующим образом:
|
||||
|
||||
<ol>
|
||||
<li>Операнды преобразуются в 32-битные целые числа, представленные последовательностью битов. Дробная часть, если она есть, отбрасывается.</li>
|
||||
<li>Для бинарных операторов -- каждый бит в первом операнде рассматривается вместе с соответствующим битом второго операнда: первый бит с первым, второй со вторым и т.п. Оператор применяется к каждой паре бит, давая соответствующий бит результата.</li>
|
||||
<li>Получившаяся в результате последовательность бит интерпретируется как обычное число.</li>
|
||||
</ol>
|
||||
|
||||
Посмотрим, как работают операторы, на примерах.
|
||||
|
||||
[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]
|
||||
|
||||
## & (Побитовое И)
|
||||
|
||||
Выполняет операцию И над каждой парой бит.
|
||||
|
||||
Результат `a & b` равен единице только когда оба бита `a` и `b` равны единице.
|
||||
|
||||
Таблица истинности для `&`:
|
||||
|
||||
<table class="standard-table">
|
||||
<tr><th>`a`</th><th>`b`</th><th>`a & b`</th></tr>
|
||||
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
|
||||
<tr><td>`0`</td><td>`1`</td><td>`0`</td></tr>
|
||||
<tr><td>`1`</td><td>`0`</td><td>`0`</td></tr>
|
||||
<tr><td>`1`</td><td>`1`</td><td>`1`</td></tr>
|
||||
</table>
|
||||
|
||||
Пример:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
9 (по осн. 10)
|
||||
= 00000000000000000000000000001001 (по осн. 2)
|
||||
14 (по осн. 10)
|
||||
= 00000000000000000000000000001110 (по осн. 2)
|
||||
--------------------------------
|
||||
14 & 9 (по осн. 10)
|
||||
= 00000000000000000000000000001000 (по осн. 2)
|
||||
= 8 (по осн. 10)
|
||||
```
|
||||
|
||||
## | (Побитовое ИЛИ)
|
||||
|
||||
Выполняет операцию ИЛИ над каждой парой бит. Результат `a | b` равен 1, если хотя бы один бит из <code>a,b</code> равен 1.
|
||||
|
||||
Таблица истинности для `|`:
|
||||
|
||||
<table class="standard-table">
|
||||
<tr><th>`a`</th><th>`b`</th><th>`a | b`</th></tr>
|
||||
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
|
||||
<tr><td>`0`</td><td>`1`</td><td>`1`</td></tr>
|
||||
<tr><td>`1`</td><td>`0`</td><td>`1`</td></tr>
|
||||
<tr><td>`1`</td><td>`1`</td><td>`1`</td></tr>
|
||||
</table>
|
||||
|
||||
Пример:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
9 (по осн. 10)
|
||||
= 00000000000000000000000000001001 (по осн. 2)
|
||||
14 (по осн. 10)
|
||||
= 00000000000000000000000000001110 (по осн. 2)
|
||||
--------------------------------
|
||||
14 | 9 (по осн. 10)
|
||||
= 00000000000000000000000000001111 (по осн. 2)
|
||||
= 15 (по осн. 10)
|
||||
```
|
||||
|
||||
## ^ (Исключающее ИЛИ)
|
||||
|
||||
Выполняет операцию "Исключающее ИЛИ" над каждой парой бит.
|
||||
|
||||
<code>a</code> Исключающее ИЛИ <code>b</code> равно 1, если только <code>a=1</code> или только <code>b=1</code>, но не оба одновременно <code>a=b=1</code>.
|
||||
|
||||
Таблица истинности для исключающего ИЛИ:
|
||||
|
||||
<table class="standard-table">
|
||||
<tr><th>`a`</th><th>`b`</th><th>`a ^ b`</th></tr>
|
||||
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
|
||||
<tr><td>`0`</td><td>`1`</td><td>`1`</td></tr>
|
||||
<tr><td>`1`</td><td>`0`</td><td>`1`</td></tr>
|
||||
<tr><td>`1`</td><td>`1`</td><td>`0`</td></tr>
|
||||
</table>
|
||||
|
||||
Как видно, оно даёт 1, если ЛИБО слева `1`, ЛИБО справа `1`, но не одновременно. Поэтому его и называют "исключающее ИЛИ".
|
||||
|
||||
Пример:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
9 (по осн. 10)
|
||||
= 00000000000000000000000000001001 (по осн. 2)
|
||||
14 (по осн. 10)
|
||||
= 00000000000000000000000000001110 (по осн. 2)
|
||||
--------------------------------
|
||||
14 ^ 9 (по осн. 10)
|
||||
= 00000000000000000000000000000111 (по осн. 2)
|
||||
= 7 (по осн. 10)
|
||||
```
|
||||
|
||||
[smart header="Исключающее ИЛИ в шифровании"]
|
||||
Исключающее или можно использовать для шифрования, так как эта операция полностью обратима. Двойное применение исключающего ИЛИ с тем же аргументом даёт исходное число.
|
||||
|
||||
Иначе говоря, верна формула: `a ^ b ^ b == a`.
|
||||
|
||||
Пускай Вася хочет передать Пете секретную информацию `data`. Эта информация заранее превращена в число, например строка интерпретируется как последовательность кодов символов.
|
||||
|
||||
Вася и Петя заранее договариваются о числовом ключе шифрования `key`.
|
||||
|
||||
Алгоритм:
|
||||
<ul>
|
||||
<li>Вася берёт двоичное представление `data` и делает операцию `data ^ key`. При необходимости `data` бьётся на части, равные по длине `key`, чтобы можно было провести побитовое ИЛИ `^` для каждой части. В JavaScript оператор `^` работает с 32-битными целыми числами, так что `data` нужно разбить на последовательность таких чисел.</li>
|
||||
<li>Результат `data ^ key` отправляется Пете, это шифровка.</li>
|
||||
</ul>
|
||||
|
||||
Например, пусть в `data` очередное число равно `9`, а ключ `key` равен `1220461917`.
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
Данные: 9 в двоичном виде
|
||||
00000000000000000000000000001001
|
||||
|
||||
Ключ: 1220461917 в двоичном виде
|
||||
01001000101111101100010101011101
|
||||
|
||||
Результат операции 9 ^ key:
|
||||
01001000101111101100010101010100
|
||||
Результат в 10-ной системе (шифровка):
|
||||
1220461908
|
||||
```
|
||||
|
||||
<ul>
|
||||
<li>Петя, получив очередное число шифровки `1220461908`, применяет к нему такую же операцию `^ key`.</li>
|
||||
<li>Результатом будет исходное число `data`.</li>
|
||||
</ul>
|
||||
|
||||
В нашем случае:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
Полученная шифровка в двоичной системе:
|
||||
9 ^ key = 1220461908
|
||||
01001000101111101100010101010100
|
||||
|
||||
Ключ: 1220461917 в двоичном виде:
|
||||
01001000101111101100010101011101
|
||||
|
||||
Результат операции 1220461917 ^ key:
|
||||
00000000000000000000000000001001
|
||||
Результат в 10-ной системе (исходное сообщение):
|
||||
9
|
||||
```
|
||||
|
||||
Конечно, такое шифрование поддаётся частотному анализу и другим методам дешифровки, поэтому современные алгоритмы используют операцию XOR `^` как одну из важных частей более сложной многоступенчатой схемы.
|
||||
[/smart]
|
||||
|
||||
|
||||
## ~ (Побитовое НЕ)
|
||||
|
||||
Производит операцию НЕ над каждым битом, заменяя его на обратный ему.
|
||||
|
||||
Таблица истинности для НЕ:
|
||||
|
||||
<table class="standard-table">
|
||||
<tr><th>`a`</th><th>`~a`</th></tr>
|
||||
<tr><td>`0`</td><td>`1`</td></tr>
|
||||
<tr><td>`1`</td><td>`0`</td></tr>
|
||||
</table>
|
||||
|
||||
Пример:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
9 (по осн. 10)
|
||||
= 00000000000000000000000000001001 (по осн. 2)
|
||||
--------------------------------
|
||||
~9 (по осн. 10)
|
||||
= 11111111111111111111111111110110 (по осн. 2)
|
||||
= -10 (по осн. 10)
|
||||
```
|
||||
|
||||
Из-за внутреннего представления отрицательных чисел получается так, что `~n == -(n+1)`.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( ~3 ); // -4
|
||||
alert( ~-1 ); // 0
|
||||
```
|
||||
|
||||
### << (Битовый сдвиг влево)
|
||||
|
||||
Операторы битового сдвига принимают два операнда. Первый -- это число для сдвига, а второй -- количество битов, которые нужно сдвинуть в первом операнде.
|
||||
|
||||
Оператор `<<` сдвигает первый операнд на указанное число битов влево. Лишние биты отбрасываются, справа добавляются нулевые биты.
|
||||
|
||||
Например, `9 << 2` даст `36`:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
|
||||
9 (по осн.10)
|
||||
= 00000000000000000000000000001001 (по осн.2)
|
||||
--------------------------------
|
||||
9 << 2 (по осн.10)
|
||||
= 00000000000000000000000000100100 (по осн.2)
|
||||
= 36 (по осн.10)
|
||||
```
|
||||
|
||||
Операция `<< 2` сдвинула и отбросила два левых нулевых бита и добавила справа два новых нулевых.
|
||||
|
||||
[smart header="Левый сдвиг почти равен умножению на 2"]
|
||||
Битовый сдвиг `<< N` обычно имеет тот же эффект, что и умножение на два `N` раз, например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 3 << 1 ); // 6, умножение на 2
|
||||
alert( 3 << 2 ); // 12, умножение на 2 два раза
|
||||
alert( 3 << 3 ); // 24, умножение на 2 три раза
|
||||
```
|
||||
|
||||
Конечно, следует иметь в виду, что побитовые операторы работают только с 32-битными числами, поэтому верхний порог такого "умножения" ограничен:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
*!*
|
||||
alert(10000000000 << 1); // -1474836480, отброшен крайний-левый бит
|
||||
*/!*
|
||||
alert(10000000000 * 2); // 20000000000, обычное умножение
|
||||
```
|
||||
|
||||
[/smart]
|
||||
|
||||
### >> (Правый битовый сдвиг, переносящий знак)
|
||||
|
||||
Этот оператор сдвигает биты вправо, отбрасывая лишние. При этом слева добавляется *копия* крайнего-левого бита.
|
||||
|
||||
Знак числа (представленный крайним-левым битом) при этом не меняется, так как новый крайний-левый бит имеет то же значение, что и исходном числе.
|
||||
|
||||
Поэтому он назван "переносящим знак".
|
||||
|
||||
Например, `9 >> 2` даст <code>2</code>:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
9 (по осн.10)
|
||||
= 00000000000000000000000000001001 (по осн.2)
|
||||
--------------------------------
|
||||
9 >> 2 (по осн.10)
|
||||
= 00000000000000000000000000000010 (по осн.2)
|
||||
= 2 (по осн.10)
|
||||
```
|
||||
|
||||
Операция `>> 2` сдвинула вправо и отбросила два правых бита `01` и добавила слева две копии первого бита `00`.
|
||||
|
||||
Аналогично, `-9 >> 2` даст `-3`:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
-9 (по осн.10)
|
||||
= 11111111111111111111111111110111 (по осн.2)
|
||||
--------------------------------
|
||||
-9 >> 2 (по осн.10)
|
||||
= 11111111111111111111111111111101 (по осн.2) = -3 (по осн.10)
|
||||
```
|
||||
|
||||
Здесь операция `>> 2` сдвинула вправо и отбросила два правых бита `11` и добавила слева две копии первого бита `11`. , Знак числа сохранён, так как крайний-левый (знаковый) бит сохранил значение `1`.
|
||||
|
||||
[smart header="Правый сдвиг почти равен целочисленному делению на 2"]
|
||||
Битовый сдвиг `>> N` обычно имеет тот же результат, что и целочисленное деление на два `N` раз:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 100 >> 1 ); // 50, деление на 2
|
||||
alert( 100 >> 2 ); // 25, деление на 2 два раза
|
||||
alert( 100 >> 3 ); // 12, деление на 2 три раза, целая часть от результата
|
||||
```
|
||||
|
||||
[/smart]
|
||||
|
||||
|
||||
### >>> (Правый сдвиг с заполнением нулями)
|
||||
|
||||
Этот оператор сдвигает биты первого операнда вправо. Лишние биты справа отбрасываются. Слева добавляются нулевые биты.
|
||||
|
||||
Знаковый бит становится равным 0, поэтому результат всегда положителен.
|
||||
|
||||
**Для неотрицательных чисел правый сдвиг с заполнением нулями `>>>` и правый сдвиг с переносом знака `>>` дадут одинаковый результат, т.к в обоих случаях слева добавятся нули.**
|
||||
|
||||
Для отрицательных чисел -- результат работы разный. Например, `-9 >>> 2` даст `1073741821`, в отличие от `-9 >> 2` (дает `-3`):
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
-9 (по осн.10)
|
||||
= 11111111111111111111111111110111 (по осн.2)
|
||||
--------------------------------
|
||||
-9 >>> 2 (по осн.10)
|
||||
= 00111111111111111111111111111101 (по осн.2)
|
||||
= 1073741821 (по осн.10)
|
||||
```
|
||||
|
||||
## Применение побитовых операторов
|
||||
|
||||
Побитовые операторы используются редко, но всё же используются.
|
||||
|
||||
Случаи применения побитовых операторов, которые мы здесь разберём, составляют большинство всех использований в JavaScript.
|
||||
|
||||
[warn header="Осторожно, приоритеты!"]
|
||||
В JavaScript побитовые операторы `^`, `&`, `|` выполняются после сравнений `==`.
|
||||
|
||||
Например, в сравнении `a == b^0` будет сначала выполнено сравнение `a == b`, а потом уже операция `^0`, как будто стоят скобки `(a == b)^0`.
|
||||
|
||||
Обычно это не то, чего мы хотим. Чтобы гарантировать желаемый порядок, нужно ставить скобки: `a == (b^0)`.
|
||||
[/warn]
|
||||
|
||||
### Маска
|
||||
|
||||
Для этого примера представим, что наш скрипт работает с пользователями.
|
||||
|
||||
У них могут быть различные роли в проекте:
|
||||
|
||||
<ul>
|
||||
<li>`Гость`</li>
|
||||
<li>`Редактор`</li>
|
||||
<li>`Админ`</li>
|
||||
</ul>
|
||||
|
||||
Каждой роли соответствует ряд доступов к статьям и функционалу сайта.
|
||||
|
||||
Например, `Гость` может лишь просматривать статьи сайта, а `Редактор` -- ещё и редактировать их, и тому подобное.
|
||||
|
||||
Что-то в таком духе:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Пользователь</th>
|
||||
<th>Просмотр статей</th>
|
||||
<th>Изменение статей</th>
|
||||
<th>Просмотр товаров</th>
|
||||
<th>Изменение товаров</th>
|
||||
<th>Управление правами</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Гость</td>
|
||||
<td>Да</td>
|
||||
<td>Нет</td>
|
||||
<td>Да</td>
|
||||
<td>Нет</td>
|
||||
<td>Нет</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Редактор</td>
|
||||
<td>Да</td>
|
||||
<td>Да</td>
|
||||
<td>Да</td>
|
||||
<td>Да</td>
|
||||
<td>Нет</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Админ</td>
|
||||
<td>Да</td>
|
||||
<td>Да</td>
|
||||
<td>Да</td>
|
||||
<td>Да</td>
|
||||
<td>Да</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Если вместо "Да" поставить `1`, а вместо "Нет" -- `0`, то каждый набор доступов описывается числом:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Пользователь</th>
|
||||
<th>Просмотр статей</th>
|
||||
<th>Изменение статей</th>
|
||||
<th>Просмотр товаров</th>
|
||||
<th>Изменение товаров</th>
|
||||
<th>Управление правами</th>
|
||||
<th>В 10-ной системе</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Гость</td>
|
||||
<td>1</td>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
<td>0</td>
|
||||
<td>0</td>
|
||||
<td> = 20</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Редактор</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td>0</td>
|
||||
<td> = 30</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Админ</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td> = 31</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
В последней колонке находится десятичное число, которое получится, если прочитать строку доступов в двоичном виде.
|
||||
|
||||
Например, доступ гостя `10100 = 20`.
|
||||
|
||||
Такая интерпретация доступов позволяет "упаковать" много информации в одно число. Это экономит память, а кроме этого -- это удобно, поскольку в дополнение к экономии -- по такому значению очень легко проверить, имеет ли посетитель заданную *комбинацию доступов*!
|
||||
|
||||
Для этого посмотрим, как в 2-ной системе представляется каждый доступ в отдельности.
|
||||
|
||||
<ul>
|
||||
<li>Доступ, соответствующий только управлению правами: `00001 (=1)` (все нули кроме `1` на позиции, соответствующей этому доступу).</li>
|
||||
<li>Доступ, соответствующий только изменению товаров: `00010 (=2)`.</li>
|
||||
<li>Доступ, соответствующий только просмотру товаров: `00100 (=4)`.</li>
|
||||
<li>Доступ, соответствующий только изменению статей: `01000 (=8)`.</li>
|
||||
<li>Доступ, соответствующий только просмотру статей: `10000 (=16)`.</li>
|
||||
</ul>
|
||||
|
||||
Доступ одновременно на просмотр и изменение статей -- это двоичное число с `1` на соответствующих позициях, то есть `access = 11000`.
|
||||
|
||||
Как правило, доступы задаются в виде констант:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
var ACCESS_ADMIN = 1; // 00001
|
||||
var ACCESS_GOODS_EDIT = 2; // 00010
|
||||
var ACCESS_GOODS_VIEW = 4; // 00100
|
||||
var ACCESS_ARTICLE_EDIT = 8; // 01000
|
||||
var ACCESS_ARTICLE_VIEW = 16; // 10000
|
||||
```
|
||||
|
||||
Из этих констант получить нужную комбинацию доступов можно при помощи операции `|`.
|
||||
|
||||
```js
|
||||
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
|
||||
```
|
||||
|
||||
Теперь, чтобы понять, есть ли в доступе `editor` нужный доступ, например управление правами -- достаточно применить к нему побитовый оператор И (`&`) с соответствующей константой.
|
||||
|
||||
Ненулевой результат будет означать, что доступ есть:
|
||||
|
||||
```
|
||||
alert(editor & ACCESS_ADMIN); // 0, доступа нет
|
||||
alert(editor & ACCESS_ARTICLE_EDIT); // 8, доступ есть
|
||||
```
|
||||
|
||||
Такая проверка работает, потому что оператор И ставит `1` на те позиции результата, на которых в обоих операндах стоит `1`.
|
||||
|
||||
**Можно проверить один из нескольких доступов.**
|
||||
|
||||
Например, проверим, есть ли права на просмотр ИЛИ изменение товаров. Соответствующие права задаются битом `1` на втором и третьем месте с конца, что даёт число `00110` (=`6` в 10-ной системе).
|
||||
|
||||
```js
|
||||
var check = ACCESS_GOODS_VIEW | ACCESS_GOODS_EDIT; // 6, 00110
|
||||
|
||||
alert( admin & check ); // не 0, значит есть доступ к просмотру ИЛИ изменению
|
||||
```
|
||||
|
||||
*Битовой маской* называют как раз комбинацию двоичных значений (`check` в примере выше), которая используется для проверки и выборки единиц на нужных позициях.
|
||||
|
||||
Маски могут быть весьма удобны.
|
||||
|
||||
В частности, их используют в функциях, чтобы одним параметром передать несколько "флагов", т.е. однобитных значений.
|
||||
|
||||
Пример вызова функции с маской:
|
||||
```js
|
||||
// найти пользователей с правами на изменение товаров или администраторов
|
||||
findUsers(ACCESS_GOODS_EDIT | ACCESS_ADMIN);
|
||||
```
|
||||
|
||||
Это довольно-таки коротко и элегантно, но, вместе с тем, применение масок налагает определённые ограничения. В частности, побитовые операторы в JavaScript работают только с 32-битными числами, а значит, к примеру, 33 доступа уже в число не упакуешь. Да и работа с двоичной системой счисления -- как ни крути, менее удобна, чем с десятичной или с обычными логическими значениями `true/false`.
|
||||
|
||||
Поэтому основная сфера применения масок -- это быстрые вычисления, экономия памяти, низкоуровневые операции, связанные с рисованием из JavaScript (3d-графика), интеграция с некоторыми функциями ОС (для серверного JavaScript), и другие ситуации, когда уже существуют функции, требующие битовую маску.
|
||||
|
||||
### Округление
|
||||
|
||||
Так как битовые операции отбрасывают десятичную часть, то их можно использовать для округления. Достаточно взять любую операцию, которая не меняет значение числа.
|
||||
|
||||
Например, двойное НЕ (`~`):
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( ~~12.345 ); // 12
|
||||
```
|
||||
|
||||
Подойдёт и Исключающее ИЛИ (`^`) с нулём:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 12.345 ^ 0 ); // 12
|
||||
```
|
||||
|
||||
Последнее даже более удобно, поскольку отлично читается:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(12.3 * 14.5 ^ 0); // (=178) "12.3 умножить на 14.5 *!*и округлить*/!*"
|
||||
```
|
||||
|
||||
У побитовых операторов достаточно низкий приоритет, он меньше чем у остальной арифметики:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 1.1 + 1.2 ^ 0 ); // 2, сложение выполнится раньше округления
|
||||
```
|
||||
|
||||
### Проверка на -1
|
||||
|
||||
[Внутренний формат](#signed-format) 32-битных чисел устроен так, что для смены знака нужно все биты заменить на противоположные ("обратить") и прибавить `1`.
|
||||
|
||||
Обращение битов -- это побитовое НЕ (`~`). То есть, при таком формате представления числа `-n = ~n + 1`. Или, если перенести единицу: `~n = -(n+1)`.
|
||||
|
||||
Как видно из последнего равенства, `~n == 0` только если `n == -1`. Поэтому можно легко проверить равенство `n == -1`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var n = 5;
|
||||
|
||||
if (~n) { // сработает, т.к. ~n = -(5+1) = -6
|
||||
alert( "n не -1" ); // выведет!
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var n = -1;
|
||||
|
||||
if (~n) { // не сработает, т.к. ~n = -(-1+1) = 0
|
||||
alert( "...ничего не выведет..." );
|
||||
}
|
||||
```
|
||||
|
||||
Проверка на `-1` пригождается, например, при поиске символа в строке. Вызов `str.indexOf("подстрока")` возвращает позицию подстроки в `str`, или `-1` если не нашёл.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Проверка";
|
||||
|
||||
if (~str.indexOf("верка")) { // Сочетание "if (~...indexOf)" читается как "если найдено"
|
||||
alert( 'найдено!' );
|
||||
}
|
||||
```
|
||||
|
||||
### Умножение и деление на степени 2
|
||||
|
||||
Оператор `a << b`, сдвигая биты, по сути умножает `a` на `2` в степени `b`.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 1 << 2 ); // 1*(2*2) = 4
|
||||
alert( 1 << 3 ); // 1*(2*2*2) = 8
|
||||
alert( 3 << 3 ); // 3*(2*2*2) = 24
|
||||
```
|
||||
|
||||
При этом следует иметь в виду, что максимальный верхний порог такого умножения меньше, чем обычно, так как побитовый оператор оперирует 32-битными целыми, в то время как обычные операторы оперируют числами длиной 64 бита.
|
||||
|
||||
Оператор сдвига в другую сторону `a >> b`, производит обратную операцию -- целочисленное деление `a` на <code>2<sup>b</sup></code>.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 8 >> 2 ); // 2 = 8/4, убрали 2 нуля в двоичном представлении
|
||||
alert( 11 >> 2 ); // 2, целочисленное деление (менее значимые биты просто отброшены)
|
||||
```
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>Бинарные побитовые операторы: `& | ^ << >> >>>`.</li>
|
||||
<li>Унарный побитовый оператор один: `~`.</li>
|
||||
</ul>
|
||||
|
||||
Как правило, битовое представление числа используется для:
|
||||
<ul>
|
||||
<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_EDIT = 2; // 00010
|
||||
var ACCESS_GOODS_VIEW = 4; // 00100
|
||||
var ACCESS_ARTICLE_EDIT = 8; // 01000
|
||||
var ACCESS_ARTICLE_VIEW = 16; // 10000
|
||||
</script>
|
||||
[/head]
|
|
@ -1,72 +1,71 @@
|
|||
# Взаимодействие с пользователем: alert, prompt, confirm
|
||||
# Interaction: alert, prompt, confirm
|
||||
|
||||
This chapter covers basic UI operations: `alert`, `prompt` and `confirm`. They allow to ask a visitor for the input and show the results.
|
||||
|
||||
В этом разделе мы рассмотрим базовые UI операции: `alert`, `prompt` и `confirm`, которые позволяют работать с данными, полученными от пользователя.
|
||||
[cut]
|
||||
## alert
|
||||
|
||||
Синтаксис:
|
||||
Syntax:
|
||||
|
||||
```js
|
||||
alert(сообщение)
|
||||
alert(message)
|
||||
```
|
||||
|
||||
`alert` выводит на экран окно с сообщением и приостанавливает выполнение скрипта, пока пользователь не нажмёт "ОК".
|
||||
`alert` shows a message and pauses the script execution until the user presses "OK".
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "Привет" );
|
||||
alert( "Hello" );
|
||||
```
|
||||
|
||||
Окно сообщения, которое выводится, является *модальным окном*. Слово "модальное" означает, что посетитель не может взаимодействовать со страницей, нажимать другие кнопки и т.п., пока не разберётся с окном. В данном случае - пока не нажмёт на "OK".
|
||||
The small special window with the message is called a *modal window*. The word "modal" means that the visitor can't interact with the rest of the page, press other buttons etc, until he deals with the window. In this case -- until he presses "OK".
|
||||
|
||||
## prompt
|
||||
|
||||
Функция prompt принимает два аргумента:
|
||||
Function `prompt` accepts two arguments:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
result = prompt(title, default);
|
||||
```
|
||||
|
||||
Она выводит модальное окно с заголовком `title`, полем для ввода текста, заполненным строкой по умолчанию `default` и кнопками OK/CANCEL.
|
||||
It shows a modal window with the given `title`, a field for text, initially filled with the `default` string and buttons OK/CANCEL.
|
||||
|
||||
Пользователь должен либо что-то ввести и нажать OK, либо отменить ввод кликом на CANCEL или нажатием [key Esc] на клавиатуре.
|
||||
The visitor may type something in the field and press OK or cancel the input by pressing a CANCEL button or the [key Esc] key.
|
||||
|
||||
**Вызов `prompt` возвращает то, что ввёл посетитель -- строку или специальное значение `null`, если ввод отменён.**
|
||||
**The call to `prompt` returns the text from the field or `null` if CANCEL is pressed.**
|
||||
|
||||
[warn header="Safari 5.1+ не возвращает `null`"]
|
||||
Единственный браузер, который не возвращает `null` при отмене ввода -- это Safari. При отсутствии ввода он возвращает пустую строку. Предположительно, это ошибка в браузере.
|
||||
[warn header="Safari 5.1+ does not return `null`"]
|
||||
Safari is the only browser which does not return `null` when the input was canceled. It returns an empty string instead.
|
||||
|
||||
Если нам важен этот браузер, то пустую строку нужно обрабатывать точно так же, как и `null`, т.е. считать отменой ввода.
|
||||
So actually we should treat empty line the same as `null`, that is: consider it a cancellation.
|
||||
[/warn]
|
||||
|
||||
Как и в случае с `alert`, окно `prompt` модальное.
|
||||
As with `alert`, the `prompt` window is modal.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var years = prompt('Сколько вам лет?', 100);
|
||||
var age = prompt('How old are you?', 100);
|
||||
|
||||
alert('Вам ' + years + ' лет!')
|
||||
alert(`You are ${age} years old!`)
|
||||
```
|
||||
|
||||
[warn header="Всегда указывайте `default`"]
|
||||
Второй параметр может отсутствовать. Однако при этом IE вставит в диалог значение по умолчанию `"undefined"`.
|
||||
[warn header="Always put `default`"]
|
||||
The second parameter is optional. But if we don't supply it, IE would insert the text `"undefined"` into the prompt.
|
||||
|
||||
Запустите этот код <u>в IE</u>, чтобы понять о чём речь:
|
||||
Run this code in Internet Explorer to see that:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var test = prompt("Тест");
|
||||
var test = prompt("Test");
|
||||
```
|
||||
|
||||
Поэтому рекомендуется *всегда* указывать второй аргумент:
|
||||
So, for IE looking good, it's recommended to always provide the second argument:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var test = prompt("Тест", ''); // <-- так лучше
|
||||
var test = prompt("Test", ''); // <-- for IE
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
|
||||
|
|
|
@ -143,11 +143,9 @@ As we can see, the "unary plus" has a priority of `15`, higher than `13` for the
|
|||
|
||||
## Assignment
|
||||
|
||||
Обратим внимание, в таблице приоритетов также есть оператор присваивания `=`.
|
||||
Let's note that an assignment `=` is also an operator. It is listed in the precedence table with the very low priority of `3`.
|
||||
|
||||
У него -- один из самых низких приоритетов: `3`.
|
||||
|
||||
Именно поэтому, когда переменную чему-либо присваивают, например, `x = 2 * 2 + 1` сначала выполнится арифметика, а уже затем -- произойдёт присвоение `=`.
|
||||
That's why when we assign a variable, like `x = 2 * 2 + 1`, the maths is done first, and then the assignment is evaluated.
|
||||
|
||||
```js
|
||||
var x = 2 * 2 + 1;
|
||||
|
@ -155,7 +153,7 @@ var x = 2 * 2 + 1;
|
|||
alert( x ); // 5
|
||||
```
|
||||
|
||||
**Возможно присваивание по цепочке:**
|
||||
It is possible to chain assignments:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -170,12 +168,14 @@ alert( b ); // 4
|
|||
alert( c ); // 4
|
||||
```
|
||||
|
||||
Такое присваивание работает справа-налево, то есть сначала вычислятся самое правое выражение `2+2`, присвоится в `c`, затем выполнится `b = c` и, наконец, `a = b`.
|
||||
The chain is evaluated from right to left: the rightmost expression `2+2` is calculated first, assigned to `c`, then `b = c` works, thus assigning it to `b`, and then `a = b`.
|
||||
|
||||
[smart header="Оператор `\"=\"` возвращает значение"]
|
||||
Все операторы возвращают значение. Вызов `x = выражение` не является исключением.
|
||||
[smart header="The assignment operator `\"=\"` returns a value"]
|
||||
An operator always returns a value. The assignment is not an exception.
|
||||
|
||||
Он записывает выражение в `x`, а затем возвращает его. Благодаря этому присваивание можно использовать как часть более сложного выражения:
|
||||
The call `x = value` writes the `value` into `x` and then returns it.
|
||||
|
||||
So it is actually possible to use an assignment as the part of the more complex expression:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -190,52 +190,51 @@ alert( a ); // 3
|
|||
alert( c ); // 0
|
||||
```
|
||||
|
||||
В примере выше результатом `(a = b + 1)` является значение, которое записывается в `a` (т.е. `3`). Оно используется для вычисления `c`.
|
||||
In the example above, the result of `(a = b + 1)` is the value which is assigned to `a` (that is `3`). It is then used to substract from `3`.
|
||||
|
||||
Забавное применение присваивания, не так ли?
|
||||
Funny code, isn't it?
|
||||
|
||||
Знать, как это работает -- стоит обязательно, а вот писать самому -- только если вы уверены, что это сделает код более читаемым и понятным.
|
||||
We should understand how it works, but don't write like that ourselves. Such usage rarely makes the code neater and more readable.
|
||||
[/smart]
|
||||
|
||||
## Remainder %
|
||||
|
||||
## Взятие остатка %
|
||||
The remainder operator `%` despite it's look does not have a relation to percents.
|
||||
|
||||
The result of `a % b` is the remainder of the integer division `a` by `b`.
|
||||
|
||||
Оператор взятия остатка `%` интересен тем, что, несмотря на обозначение, никакого отношения к процентам не имеет.
|
||||
|
||||
Его результат `a % b` -- это остаток от деления `a` на `b`.
|
||||
|
||||
Например:
|
||||
For instance:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 5 % 2 ); // 1, остаток от деления 5 на 2
|
||||
alert( 8 % 3 ); // 2, остаток от деления 8 на 3
|
||||
alert( 6 % 3 ); // 0, остаток от деления 6 на 3
|
||||
alert( 5 % 2 ); // 1 is a remainder of 5 divided by 2
|
||||
alert( 8 % 3 ); // 2 is a remainder of 8 divided by 3
|
||||
alert( 6 % 3 ); // 0 is a remainder of 6 divided by 3
|
||||
```
|
||||
|
||||
## Increment/decrement: ++, --
|
||||
|
||||
## Инкремент/декремент: ++, --
|
||||
Increasing or decreasing a number by one is one of the most common numeric operations in JavaScript as well as in many other languages.
|
||||
|
||||
Одной из наиболее частых операций в JavaScript, как и во многих других языках программирования, является увеличение или уменьшение переменной на единицу.
|
||||
So, there are special operators for that:
|
||||
|
||||
Для этого существуют даже специальные операторы:
|
||||
<ul>
|
||||
<li>**Инкремент** `++` увеличивает на 1:
|
||||
<li>**Increment** `++` increases by 1:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
var i = 2;
|
||||
i++; // более короткая запись для i = i + 1.
|
||||
i++; // works same as i = i + 1, but shorter
|
||||
alert(i); // 3
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>**Декремент** `--` уменьшает на 1:
|
||||
<li>**Decrement** `--` decreases by 1:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
var i = 2;
|
||||
i--; // более короткая запись для i = i - 1.
|
||||
i--; // works same as i = i - 1, but shorter
|
||||
alert(i); // 1
|
||||
```
|
||||
|
||||
|
@ -243,17 +242,22 @@ alert(i); // 1
|
|||
</ul>
|
||||
|
||||
[warn]
|
||||
Инкремент/декремент можно применить только к переменной.
|
||||
Код `5++` даст ошибку.
|
||||
Increment/decrement can be applied only to a variable.
|
||||
|
||||
An attempt to use it on a value like `5++` will give an error.
|
||||
[/warn]
|
||||
|
||||
Вызывать эти операторы можно не только после, но и перед переменной: `i++` (называется "постфиксная форма") или `++i` ("префиксная форма").
|
||||
These operators can be called both after and before the variable.
|
||||
|
||||
Обе эти формы записи делают одно и то же: увеличивают на `1`.
|
||||
When the operator goes after the variable, it is called a "postfix form": `i++`. The "prefix form" is when the operator is before the variable: `++i`.
|
||||
|
||||
Тем не менее, между ними существует разница. Она видна только в том случае, когда мы хотим не только увеличить/уменьшить переменную, но и использовать результат в том же выражении.
|
||||
Both of these records do the same: increase `i` by `1`.
|
||||
|
||||
Например:
|
||||
Is there any difference? Actually, yes. It can be seen when we not only increment/decrement the variable, but also use the result of the operator.
|
||||
|
||||
The prefix form returns the new value, while the postfix form returns the old value (pre-increment/decrement).
|
||||
|
||||
Let's see the examples:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -263,11 +267,9 @@ var a = ++i; // (*)
|
|||
alert(a); // *!*2*/!*
|
||||
```
|
||||
|
||||
В строке `(*)` вызов `++i` увеличит переменную, а *затем* вернёт ее значение в `a`. Так что в `a` попадёт значение `i` *после* увеличения.
|
||||
Here in the line `(*)` the prefix call `++i` increments `i` and returns the new value that is `2`. So the `alert` shows `2`.
|
||||
|
||||
**Постфиксная форма `i++` отличается от префиксной `++i` тем, что возвращает старое значение, бывшее до увеличения.**
|
||||
|
||||
В примере ниже в `a` попадёт старое значение `i`, равное `1`:
|
||||
Now let's use the postfix form:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -277,19 +279,21 @@ var a = i++; // (*)
|
|||
alert(a); // *!*1*/!*
|
||||
```
|
||||
|
||||
In the line `(*)` the *postfix* form `i++` also increments `i`, but returns the old value (it was `1`). So the `alert` shows `1`.
|
||||
|
||||
<ul>
|
||||
<li>Если результат оператора не используется, а нужно только увеличить/уменьшить переменную -- без разницы, какую форму использовать:
|
||||
<li>If the result of increment/decrement is not used, then there is no difference which form to use:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 0;
|
||||
i++;
|
||||
++i;
|
||||
alert( i ); // 2
|
||||
alert( i ); // 2, the lines above did the same
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>Если хочется тут же использовать результат, то нужна префиксная форма:
|
||||
<li>If we'd like to use the result of the operator right now, then we need the prefix form:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -298,7 +302,7 @@ alert( ++i ); // 1
|
|||
```
|
||||
|
||||
</li>
|
||||
<li>Если нужно увеличить, но нужно значение переменной *до увеличения* -- постфиксная форма:
|
||||
<li>If we'd like to increment, but use the previous value, then we need te postfix form:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -309,9 +313,8 @@ alert( i++ ); // 0
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
[smart header="Инкремент/декремент можно использовать в любых выражениях"]
|
||||
|
||||
При этом он имеет более высокий приоритет и выполняется раньше, чем арифметические операции:
|
||||
[smart header="Increment/decrement can be used anywhere"]
|
||||
An attentive reader could note that `++` (as well as `--`) can use as a part of any expression. It's priority is higher than most other arithmetical operations:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -319,17 +322,18 @@ var i = 1;
|
|||
alert( 2 * ++i ); // 4
|
||||
```
|
||||
|
||||
|
||||
Compare with:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 1;
|
||||
alert( 2 * i++ ); // 2, выполнился раньше но значение вернул старое
|
||||
alert( 2 * i++ ); // 2, because i++ returns the pre-increment value
|
||||
```
|
||||
|
||||
При этом, нужно с осторожностью использовать такую запись, потому что в более длинной строке при быстром "вертикальном" чтении кода легко пропустить такой `i++`, и будет неочевидно, что переменая увеличивается.
|
||||
We should really weigh all pro and contra using such notation, because it makes the code less neat.
|
||||
During a fast "vertical" code reading it's easy to miss such `i++`, and it would be not obvious that the variable increases.
|
||||
|
||||
Три строки, по одному действию в каждой -- длиннее, зато нагляднее:
|
||||
Three lines, one line -- one action is much better:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -339,30 +343,35 @@ i++;
|
|||
```
|
||||
[/smart]
|
||||
|
||||
## Побитовые операторы
|
||||
## Bitwise operators
|
||||
|
||||
Побитовые операторы рассматривают аргументы как 32-разрядные целые числа и работают на уровне их внутреннего двоичного представления.
|
||||
Bitwise operators treat arguments as 32-bit interger numbers and work on the level on their binary representation.
|
||||
|
||||
Эти операторы не являются чем-то специфичным для JavaScript, они поддерживаются в большинстве языков программирования.
|
||||
These operators are not JavaScript-specific. They are supported in most programming languages.
|
||||
|
||||
The list of operators:
|
||||
|
||||
Поддерживаются следующие побитовые операторы:
|
||||
<ul>
|
||||
<li>AND(и) ( `&` )</li>
|
||||
<li>OR(или) ( `|` )</li>
|
||||
<li>XOR(побитовое исключающее или) ( `^` )</li>
|
||||
<li>NOT(не) ( `~` )</li>
|
||||
<li>LEFT SHIFT(левый сдвиг) ( `<<` )</li>
|
||||
<li>RIGHT SHIFT(правый сдвиг) ( `>>` )</li>
|
||||
<li>ZERO-FILL RIGHT SHIFT(правый сдвиг с заполнением нулями) ( `>>>` )</li>
|
||||
<li>AND ( `&` )</li>
|
||||
<li>OR ( `|` )</li>
|
||||
<li>XOR ( `^` )</li>
|
||||
<li>NOT ( `~` )</li>
|
||||
<li>LEFT SHIFT ( `<<` )</li>
|
||||
<li>RIGHT SHIFT ( `>>` )</li>
|
||||
<li>ZERO-FILL RIGHT SHIFT ( `>>>` )</li>
|
||||
</ul>
|
||||
|
||||
Они используются редко, поэтому вынесены в отдельную главу [](/bitwise-operators).
|
||||
These operators are used quite rarely. We won't need them any time soon.
|
||||
|
||||
Their study implies the knowledge of low-level number representation, and it would be suboptimal to delve into that now.
|
||||
|
||||
If a reader wants to learn the right now, they are explained in the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article in MDN. It is advised to learn this topic later when such a need arises.
|
||||
|
||||
## Сокращённая арифметика с присваиванием
|
||||
## Modify-in-place
|
||||
|
||||
Часто нужно применить оператор к переменной и сохранить результат в ней же, например:
|
||||
We often need to apply an operator to a variable and keep the result in it.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
var n = 2;
|
||||
|
@ -370,20 +379,20 @@ n = n + 5;
|
|||
n = n * 2;
|
||||
```
|
||||
|
||||
Эту запись можно укоротить при помощи совмещённых операторов, вот так:
|
||||
This notation can be shortened using operators `+=` and *=`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var n = 2;
|
||||
n += 5; // теперь n=7 (работает как n = n + 5)
|
||||
n *= 2; // теперь n=14 (работает как n = n * 2)
|
||||
n += 5; // now n=7 (same as n = n + 5)
|
||||
n *= 2; // now n=14 (same as n = n * 2)
|
||||
|
||||
alert( n ); // 14
|
||||
```
|
||||
|
||||
Так можно сделать для операторов `+,-,*,/` и бинарных `<<,>>,>>>,&,|,^`.
|
||||
Short "modify-and-assign" operators exist for `+,-,*,/` and bitwise `<<,>>,>>>,&,|,^`.
|
||||
|
||||
Вызов с присваиванием имеет в точности такой же приоритет, как обычное присваивание, то есть выполнится после большинства других операций:
|
||||
The call has the same precedence as a normal assignment, so it executes after most other calculations:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -394,34 +403,36 @@ alert( n ); // 16 (n = 2 * 8)
|
|||
```
|
||||
|
||||
|
||||
## Оператор запятая
|
||||
## Comma
|
||||
|
||||
Один из самых необычных операторов -- запятая `','`.
|
||||
The comma operator `','` is one of most rare and unusual ones. But we just have to explain it here, because it is used to write shorter code, and we need to understand what's going on.
|
||||
|
||||
Его можно вызвать явным образом, например:
|
||||
The comma operator allows to evaluate several expressions, dividing them with a comma `','`. Each of them is evaluated, but result of the last one is returned.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
*!*
|
||||
a = (5, 6);
|
||||
a = (1+2, 3+4);
|
||||
*/!*
|
||||
|
||||
alert( a );
|
||||
alert( a ); // 7 (the result of 3+4)
|
||||
```
|
||||
|
||||
Запятая позволяет перечислять выражения, разделяя их запятой `','`. Каждое из них -- вычисляется и отбрасывается, за исключением последнего, которое возвращается.
|
||||
Here, the first expression `1+2` is evaluated and thrown away, while the last one `3+4` is evaluated and returned as the result.
|
||||
|
||||
Запятая -- единственный оператор, приоритет которого ниже присваивания. В выражении `a = (5,6)` для явного задания приоритета использованы скобки, иначе оператор `'='` выполнился бы до запятой `','`, получилось бы `(a=5), 6`.
|
||||
Why do we need such an operator which throws away everything except the last part?
|
||||
|
||||
Зачем же нужен такой странный оператор, который отбрасывает значения всех перечисленных выражений, кроме последнего?
|
||||
Usually it is used in more complex constructs to put several actions in one line.
|
||||
|
||||
Обычно он используется в составе более сложных конструкций, чтобы сделать несколько действий в одной строке. Например:
|
||||
For example:
|
||||
|
||||
```js
|
||||
// три операции в одной строке
|
||||
// three operations in one line
|
||||
for (*!*a = 1, b = 3, c = a*b*/!*; a < 10; a++) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Такие трюки используются во многих JavaScript-фреймворках для укорачивания кода.
|
||||
Such tricks are used in many JavaScript frameworks, that's why we mention about them. But usually they don't benefit to code readability.
|
||||
|
|
|
@ -1,180 +1,151 @@
|
|||
# Операторы сравнения и логические значения
|
||||
# Comparisons and boolean values
|
||||
|
||||
В этом разделе мы познакомимся с операторами сравнения и с логическими значениями, которые такие операторы возвращают.
|
||||
In this chapter we'll meet the comparison operators and boolean values that are their results.
|
||||
|
||||
[cut]
|
||||
|
||||
Многие операторы сравнения знакомы нам из математики:
|
||||
Many comparison operators we know from maths:
|
||||
|
||||
<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>Greater/less than: <code>a > b</code>, <code>a < b</code>.</li>
|
||||
<li>Greater/less than or equals: <code>a >= b</code>, <code>a <= b</code>.</li>
|
||||
<li>Equals: `a == b` (please note the double equation sign `'='`. A single symbol `a = b` would mean an assignment).</li>
|
||||
<li>Not equals. In maths the sign is <code>≠</code>, in JavaScript we use an assignment with an exclamation before it: <code>a != b</code>.</li>
|
||||
</ul>
|
||||
|
||||
## Логические значения
|
||||
## Boolean values
|
||||
|
||||
Как и другие операторы, сравнение возвращает значение. Это значение имеет *логический* тип.
|
||||
Just as other operators, a comparison returns a value.
|
||||
|
||||
The value has the boolean type. The term "logical type" is also used and means the same.
|
||||
|
||||
There are only two logical values:
|
||||
|
||||
Существует всего два логических значения:
|
||||
<ul>
|
||||
<li>`true` -- имеет смысл "да", "верно", "истина".</li>
|
||||
<li>`false` -- означает "нет", "неверно", "ложь".</li>
|
||||
<li>`true` -- means "yes", "correct" or "the truth".</li>
|
||||
<li>`false` -- means "no", "wrong" or "a lie".</li>
|
||||
</ul>
|
||||
|
||||
Например:
|
||||
For example:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 2 > 1 ); // true, верно
|
||||
alert( 2 == 1 ); // false, неверно
|
||||
alert( 2 != 1 ); // true
|
||||
alert( 2 > 1 ); // true (correct)
|
||||
alert( 2 == 1 ); // false (wrong)
|
||||
alert( 2 != 1 ); // true (correct)
|
||||
```
|
||||
|
||||
Логические значения можно использовать и напрямую, присваивать переменным, работать с ними как с любыми другими:
|
||||
Boolean values can be assigned directly, just like any other values:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = true; // присваивать явно
|
||||
var a = true; // assign directly
|
||||
|
||||
var b = 3 > 4; // или как результат сравнения
|
||||
var b = 3 > 4; // assign the result of the comparison
|
||||
alert( b ); // false
|
||||
|
||||
alert( a == b ); // (true == false) неверно, выведет false
|
||||
alert( a == b ); // false (cause a=true, b=false)
|
||||
```
|
||||
|
||||
## Сравнение строк
|
||||
## Strings comparison
|
||||
|
||||
Строки сравниваются побуквенно:
|
||||
Strings are compared letter-by-letter, alphabetically.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 'Б' > 'А' ); // true
|
||||
alert( 'Z' > 'A' ); // true
|
||||
alert( 'Glow' > 'Glee' ); // true
|
||||
alert( 'Bee' > 'Be' ); // true
|
||||
```
|
||||
|
||||
[warn header="Осторожно, Unicode!"]
|
||||
Аналогом "алфавита" во внутреннем представлении строк служит кодировка, у каждого символа -- свой номер (код). JavaScript использует кодировку [Unicode](http://ru.wikipedia.org/wiki/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4).
|
||||
The algorithm to compare two strings is simple:
|
||||
<ol>
|
||||
<li>Compare the first characters of both strings. If the first one is greater(or less), then the first string is greater(or less).</li>
|
||||
<li>If first characters are equal, compare the second characters the same way.</li>
|
||||
<li>Repeat until the end of any string.</li>
|
||||
<li>If both strings ended simultaneously, then they are equal. Otherwise the longer string is greater.</li>
|
||||
</ol>
|
||||
|
||||
При этом сравниваются *численные коды символов*. В частности, код у символа `Б` больше, чем у `А`, поэтому и результат сравнения такой.
|
||||
In the example above, the comparison `'Z' > 'A'` gets the result at the first step.
|
||||
|
||||
**В кодировке Unicode обычно код у строчной буквы больше, чем у прописной.**
|
||||
Strings `"Glow"` and `"Glee"` are compared character-by-character:
|
||||
<ol>
|
||||
<li>`G` is the same as `G`.</li>
|
||||
<li>`l` is the same as `l`.</li>
|
||||
<li>`o` is greater than `e`. Stop here. The first string is greater.</li>
|
||||
</ol>
|
||||
|
||||
Поэтому регистр имеет значение:
|
||||
[smart header="Lexicographical ordering"]
|
||||
The strings are compared in the so-called "lexicographical" or "dictionary" order.
|
||||
|
||||
It implies that the greater string is the one which goes further in the dictionary. So, for example, `Z` goes after `A` in a dictionary. And `Glow` goes after `Glee`.
|
||||
|
||||
But the actual situation is a little bit more complex with that, because of internal encoding details. We'll get back in the chapter [](/strings) where we study strings specifically.
|
||||
[/smart]
|
||||
|
||||
|
||||
## Comparison of different types
|
||||
|
||||
When compared values are of different types, they get autoconverted to numbers.
|
||||
|
||||
Strings are converted the same way as unary plus does. We discussed that in the chapter [](/operators).
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 'а' > 'Я' ); // true, строчные буквы больше прописных
|
||||
alert( '2' > 1 ); // true, string '2' becomes a number 2
|
||||
alert( '01' == 1 ); // true, string '01' becomes a number 1
|
||||
```
|
||||
|
||||
Для корректного сравнения символы должны быть в одинаковом регистре.
|
||||
[/warn]
|
||||
|
||||
Если строка состоит из нескольких букв, то сравнение осуществляется как в телефонной книжке или в словаре. Сначала сравниваются первые буквы, потом вторые, и так далее, пока одна не будет больше другой.
|
||||
|
||||
Иными словами, больше -- та строка, которая в телефонной книге была бы на большей странице.
|
||||
|
||||
Например:
|
||||
<ul>
|
||||
<li>Если первая буква первой строки больше -- значит первая строка больше, независимо от остальных символов:
|
||||
For boolean values, `true` becomes `1` and `false` becomes `0`, so:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 'Банан' > 'Аят' );
|
||||
alert( true == 1 ); // true
|
||||
alert( false == 0 ); // true
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>Если одинаковы -- сравнение идёт дальше. Здесь оно дойдёт до третьей буквы:
|
||||
Rules for numeric conversion are to be discussed in more details in the chapter [](/types-conversion).
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 'Вася' > 'Ваня' ); // true, т.к. 'с' > 'н'
|
||||
```
|
||||
## Strict equality
|
||||
|
||||
</li>
|
||||
<li>При этом любая буква больше отсутствия буквы:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 'Привет' > 'Прив' ); // true, так как 'е' больше чем "ничего".
|
||||
```
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
Такое сравнение называется *лексикографическим*.
|
||||
|
||||
|
||||
[warn]
|
||||
Обычно мы получаем значения от посетителя в виде строк. Например, `prompt` возвращает *строку*, которую ввел посетитель.
|
||||
|
||||
Числа, полученные таким образом, в виде строк сравнивать нельзя, результат будет неверен. Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "2" > "14" ); // true, неверно, ведь 2 не больше 14
|
||||
```
|
||||
|
||||
В примере выше `2` оказалось больше `14`, потому что строки сравниваются посимвольно, а первый символ `'2'` больше `'1'`.
|
||||
|
||||
Правильно было бы преобразовать их к числу явным образом. Например, поставив перед ними `+`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( +"2" > +"14" ); // false, теперь правильно
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
## Сравнение разных типов
|
||||
|
||||
При сравнении значений разных типов, используется числовое преобразование. Оно применяется к обоим значениям.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( '2' > 1 ); // true, сравнивается как 2 > 1
|
||||
alert( '01' == 1 ); // true, сравнивается как 1 == 1
|
||||
alert( false == 0 ); // true, false становится числом 0
|
||||
alert( true == 1 ); // true, так как true становится числом 1.
|
||||
```
|
||||
|
||||
Тема преобразований типов будет продолжена далее, в главе [](/types-conversion).
|
||||
|
||||
## Строгое равенство
|
||||
|
||||
В обычном операторе `==` есть "проблема"" -- он не может отличить `0` от `false`:
|
||||
A regular equality `==` has a "problem": it cannot differ `0` from `false`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 0 == false ); // true
|
||||
```
|
||||
|
||||
Та же ситуация с пустой строкой:
|
||||
|
||||
The same thing with an empty string:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( '' == false ); // true
|
||||
```
|
||||
|
||||
Это естественное следствие того, что операнды разных типов преобразовались к числу. Пустая строка, как и `false`, при преобразовании к числу дают `0`.
|
||||
That's the natural consequence of what we've seen before. Operands of different types are converted to a number. An empty string, just like `false`, becomes a zero.
|
||||
|
||||
Что же делать, если всё же нужно отличить `0` от `false`?
|
||||
What to do if we'd like to differentiate `0` from `false`?
|
||||
|
||||
**Для проверки равенства без преобразования типов используются операторы строгого равенства `===` (тройное равно) и `!==`.**
|
||||
**A strict equality operator `===` checks the equality without type conversion.**
|
||||
|
||||
Если тип разный, то они всегда возвращают `false`:
|
||||
In other words, `a === b` always returns `false` if `a` and `b` belong to differnt types.
|
||||
|
||||
Let's try it:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 0 === false ); // false, т.к. типы различны
|
||||
alert( 0 === false ); // false, because the types are different
|
||||
```
|
||||
|
||||
Строгое сравнение предпочтительно, если мы хотим быть уверены, что "сюрпризов" не будет.
|
||||
There also exists a "strict non-equality" operator `!==`, as an analogy for `!=`.
|
||||
|
||||
## Сравнение с null и undefined
|
||||
The string equality is one character longer, but it's more obvious what's going on.
|
||||
|
||||
## Comparison with null and undefined
|
||||
|
||||
Проблемы со специальными значениями возможны, когда к переменной применяется операция сравнения `> < <= >=`, а у неё может быть как численное значение, так и `null/undefined`.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue