Выражения и операторы - JavaScript

Выражения и операторы
=====================

* « Предыдущая статья
* Следующая статья »

Эта глава описывает выражения и операторы языка JavaScript, такие как операторы присваивания, сравнения, арифметические, битовые, логические, строчные, и различные специальные операторы.

Полный и детальный список операторов и выражений также доступен в этом руководстве.

Операторы
---------

В JavaScript есть следующие типы операторов. Данный подраздел описывает каждый тип и содержит информацию об их приоритетах друг над другом.

* Операторы присваивания
* Операторы сравнения
* Арифметические операторы
* Битовые (поразрядные) операторы
* Логические операторы
* Строковые операторы
* Условный (тернарный) оператор
* Оператор запятая
* Унарные операторы
* Операторы отношения
* Приоритет операторов

JavaScript поддерживает бинарные и унарные операторы, а также ещё один специальный тернарный оператор - условный оператор. Бинарная операция использует два операнда, один перед оператором и другой за ним:
```

operand1 operator operand2
```

Например: `3+4` или `x*y`.

В свою очередь унарная операция использует один операнд, перед или после оператора:
```

operator operand
```

или
```

operand operator
```

Например: `x++` или `++x`.

### Операторы присваивания

В результате операции присваивания операнду слева от оператора присваивания (en-US) (знак "=") устанавливается значение , которое берётся из правого операнда. Основным оператором присваивания является =, он присваивает значение правого операнда операнду, находящемуся слева. Таким образом, выражение x = y означает, что x присваивается значение y.

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

| Имя | Сокращённый оператор | Смысл |
| --- | --- | --- |
| Присваивание (en-US) | `x = y` | `x = y` |
| Присваивание со сложением (en-US) | `x += y` | `x = x + y` |
| Присваивание с вычитанием (en-US) | `x -= y` | `x = x - y` |
| Присваивание с умножением (en-US) | `x *= y` | `x = x * y` |
| Присваивание с делением (en-US) | `x /= y` | `x = x / y` |
| Присваивание по модулю (en-US) | `x %= y` | `x = x % y` |
| Присваивание с левым сдвигом (en-US) | `x <<= y` | `x = x << y` |
| Присваивание с правым сдвигом (en-US) | `x >>= y` | `x = x >> y` |
| Присваивание с беззнаковым сдвигом вправо (en-US) | `x >>>= y` | `x = x >>> y` |
| Присваивание с побитовым AND (en-US) | `x &= y` | `x = x & y` |
| Присваивание с побитовым XOR (en-US) | `x ^= y` | `x = x ^ y` |
| Присваивание с побитовым OR (en-US) | `x |= y` | `x = x | y` |

#### Деструктуризация

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

```js
var foo = ["one", "two", "three"];

// без деструктуризации
var one = foo[0];
var two = foo[1];
var three = foo[2];

// с деструктуризацией
var [one, two, three] = foo;
```

### Операторы сравнения

Оператор сравнения (en-US) сравнивает свои операнды и возвращает логическое значение, базируясь на истинности сравнения. Операнды могут быть числами, строками, логическими величинами или объектами. Строки сравниваются на основании стандартного лексикографического порядка, используя Unicode-значения. В большинстве случаев, если операнды имеют разный тип, то JavaScript пробует преобразовать их в тип, подходящий для сравнения. Такое поведение обычно происходит при сравнении числовых операндов. Единственным исключением из данного правила является сравнение с использованием операторов `===` и `!==`, которые производят строгое сравнение на равенство или неравенство. Эти операторы не пытаются преобразовать операнды перед их сравнением. Следующая таблица описывает операторы сравнения в контексте следующего примера кода:

```js
var var1 = 3,
  var2 = 4;
```

| Оператор | Описание | Примеры, возвращающие true |
| --- | --- | --- |
| Равно (`==`) | Возвращает true, если операнды равны. | `3 == var1` `"3" == var1` `3 == '3'` |
| Не равно (`!=`) | Возвращает true, если операнды не равны. | `var1 != 4 var2 != "3"` |
| Строго равно (`===`) | Возвращает true, если операнды равны и имеют одинаковый тип. Смотрите также `Object.is` и sameness in JS. | `3 === var1` |
| Строго не равно(`!==`) | Возвращает true, если операнды не равны и/или имеют разный тип. | `var1 !== "3" 3 !== '3'` |
| Больше (`>`) | Возвращает true, если операнд слева больше операнда справа. | `var2 > var1 "12" > 2` |
| Больше или равно (`>=`) | Возвращает true, если операнд слева больше или равен операнду справа. | `var2 >= var1 var1 >= 3` |
| Меньше (`<`) | Возвращает true, если операнд слева меньше операнда справа. | `var1 < var2 "2" < 12` |
| Меньше или равно (`<=`) | Возвращает true, если операнд слева меньше или равен операнду справа. | `var1 <= var2 var2 <= 5` |

**Примечание:** (**=>**) не оператор, а нотация Стрелочных функций.

### Арифметические операторы

Арифметические операторы (en-US) используют в качестве своих операндов числа (также литералы или переменные) и в качестве результата возвращают одно числовое значение. Стандартными арифметическими операторами являются сложение (+), вычитание (-), умножение (\*), и деление (/). При работе с числами с плавающей точкой эти операторы работают аналогично их работе в большинстве других языках программирования (обратите внимание, что деление на ноль возвращает бесконечность `Infinity`). Например:

```js
console.log(1 / 2); /\* возвращает 0.5 \*/
console.log(1 / 2 == 1.0 / 2.0); /\* возвращает true \*/
```

Кроме того, JavaScript позволяет использовать следующие арифметические операторы, представленные в таблице:

| Оператор | Описание | Пример |
| --- | --- | --- |
| Остаток от деления (en-US) (`%`) | Бинарный оператор. Возвращает целочисленный остаток от деления двух операндов. | 12 % 5 вернёт 2. |
| Инкремент (en-US) (`++`) | Унарный оператор. Добавляет единицу к своему операнду. Если используется в качестве префикса (`++x`), то возвращает значение операнда с добавленной к нему единицей; а в случае применения в качестве окончания (`x++`) возвращает значение операнда перед добавлением к нему единицы. | `Если x` равно 3, тогда `++x` установит значение `x` равным 4 и вернёт 4, напротив `x++` вернёт 3 и потом установит значение `x` равным 4. |
| Декремент (en-US) (`--`) | Унарный оператор. Вычитает единицу из значения своего операнда. Логика данного оператора аналогична оператору инкремента. | Если `x` равно 3, тогда `--x` установит значение `x` равным 2 и вернёт 2, напротив `x--` вернёт 3 и потом установит значение `x` равным 2. |
| Унарный минус (en-US)`-` | Унарный оператор. Возвращает отрицательное значение своего операнда. | Если `x` равно 3, тогда `-x` вернёт -3. |
| Унарный плюс (en-US) (`+`) | Унарный оператор. Пытается конвертировать операнд в число, если он ещё не оно. | `+"3"` вернёт `3`. `+true` вернёт `1.` |
| Возведение в степень (en-US) (`**`)
Экспериментальная возможность
 | Возводит `основание` в показатель `степени`, как, `основаниестепень` | `2 ** 3` вернёт `8`. `10 ** -1` вернёт `0.1`. |

### Битовые (поразрядные) операторы

Битовые операторы (en-US) обрабатывают свои операнды как последовательности из 32 бит (нулей и единиц), а не как десятичные, шестнадцатеричные или восьмеричные числа. Например, десятичное число 9 имеет двоичное представление 1001. Битовые операторы выполняют операции над таким двоичным представлением, но результат возвращают как обычное числовое значение JavaScript.

Следующая таблица обобщает битовые операторы JavaScript.

| Оператор | Использование | Описание |
| --- | --- | --- |
| Побитовое И (en-US) | `a & b` | Возвращает единицу в каждой битовой позиции, для которой соответствующие биты обеих операндов являются единицами. |
| Побитовое ИЛИ (en-US) | `a | b` | Возвращает единицу в каждой битовой позиции, для которой один из соответствующих битов или оба бита операндов являются единицами. |
| Исключающее ИЛИ (en-US) | `a ^ b` | Возвращает единицу в каждой битовой позиции, для которой только один из соответствующих битов операндов является единицей. |
| Побитовое НЕ (en-US) | `~ a` | Заменяет биты операнда на противоположные. |
| Сдвиг влево (en-US) | `a << b` | Сдвигает `a` в двоичном представлении на `b` бит влево, добавляя справа нули. |
| Сдвиг вправо с переносом знака (en-US) | `a >> b` | Сдвигает `a` в двоичном представлении на `b` бит вправо, отбрасывая сдвигаемые биты. |
| Сдвиг вправо с заполнением нулями (en-US) | `a >>> b` | Сдвигает `a` в двоичном представлении на `b` бит вправо, отбрасывая сдвигаемые биты и добавляя слева нули. |

#### Битовые логические операторы

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

* Операнды преобразуются в 32-битные целые числа и представляются в виде последовательности бит (нулей и единиц). Числа, имеющие более 32 битов будут сокращены. Например, следующее число имеет больше 32 битов и сконвертируется в 32-х битное:
```

До    : 11100110111110100000000000000110000000000001
После :             10100000000000000110000000000001
```

* Каждый бит первого операнда связывается с соответствующим битом второго операнда: первый бит с первым битом, второй бит - со вторым, и так далее.
* К каждой паре бит применяется заданный оператор, и побитово формируется итоговый результат.

Например, двоичным представлением числа 9 является 1001, а двоичным представлением пятнадцати - 1111. Результаты применения к этим числам битовых логических операторов выглядят следующим образом:

| Выражение | Результат | Двоичное описание |
| --- | --- | --- |
| `15 & 9` | `9` | `1111 & 1001 = 1001` |
| `15 | 9` | `15` | `1111 | 1001 = 1111` |
| `15 ^ 9` | `6` | `1111 ^ 1001 = 0110` |
| `~15` | `-16` | `~``00000000...``00001111 = ``1111``1111``...``11110000` |
| `~9` | `-10` | `~``00000000``...``0000``1001 = ``1111``1111``...``1111``0110` |

Обратите внимание, что все 32 бита преобразуются с использованием битового оператора НЕ, и что величины с наиболее значимым (самым левым) битом равным 1 представляют собой отрицательные числа (в представлении дополнения до двух).

#### Битовые операторы сдвига

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

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

Операторы сдвига перечислены в следующей таблице.

| Оператор | Описание | Пример |
| --- | --- | --- |
| Сдвиг влево (en-US) (`<<`) | Данный оператор сдвигает первый операнд на указанное количество бит влево. Излишние биты, сдвинутые влево, отбрасываются. Справа число дополняется нулевыми битами. | `9<<2` равно 36, так как 1001 после сдвига на 2 бита влево превращается в 100100, что соответствует числу 36. |
| Сдвиг вправо с переносом знака (en-US) (`>>`) | Данный оператор сдвигает первый операнд на указанное количество бит вправо. Излишние биты, сдвинутые вправо, отбрасываются. Слева число дополняется копиями крайнего слева бита. | `9>>2` равно 2, так как 1001 после сдвига на 2 бита вправо превращается в 10, что соответствует числу 2. Подобным же образом `-9>>2` равно -3, так как знак сохраняется. |
| Сдвиг вправо с заполнением нулями (en-US) (`>>>`) | Данный оператор сдвигает первый операнд на указанное количество бит вправо. Излишние биты, сдвинутые вправо, отбрасываются. Слева число дополняется нулевыми битами. | `19>>>2` равно 4, так как 10011 после сдвига на 2 бита вправо превращается в 100, что соответствует числу 4. Для неотрицательных чисел сдвиг вправо с заполнением нулями и сдвиг вправо с переносом знака дают одинаковый результат. |

### Логические операторы

Логические операторы (en-US) обычно используются с булевыми (логическими) значениями; при этом возвращаемое ими значение также является булевым. Однако операторы && и || фактически возвращают значение одного из операндов, поэтому, если эти операторы используются с небулевыми величинами, то возвращаемая ими величина также может быть не булевой. Логические операторы описаны в следующей таблице.

| Оператор | Использование | Описание |
| --- | --- | --- |
| Логическое И (en-US)(`&&`) | `expr1 && expr2` | (Логическое И) Возвращает операнд `expr1`, если он может быть преобразован в `false`; в противном случае возвращает операнд `expr2`. Таким образом, при использовании булевых величин в качестве операндов, оператор `&&` возвращает `true`, если оба операнда `true`; в противном случае возвращает `false`. |
| Логическое ИЛИ (en-US)(`||`) | `expr1 || expr2` | (Логическое ИЛИ) Возвращает операнд `expr1`, если он может быть преобразован в `true`; в противном случае возвращает операнд `expr2`. Таким образом, при использовании булевых величин в качестве операндов, оператор `||` возвращает `true`, если один из операндов `true`; если же оба `false`, то возвращает `false`. |
| Логическое НЕ (en-US) (`!`) | `!expr` | (Логическое НЕ) Возвращает `false`, если операнд может быть преобразован в `true`; в противном случае возвращает `true`. |

Примерами выражений, которые могут быть преобразованы в false являются: null, 0, NaN, пустая строка ("") или undefined.

Следующий код демонстрирует примеры использования оператора && (логическое И).

```js
var a1 = true && true; // t && t возвращает true
var a2 = true && false; // t && f возвращает false
var a3 = false && true; // f && t возвращает false
var a4 = false && 3 == 4; // f && f возвращает false
var a5 = "Cat" && "Dog"; // t && t возвращает Dog
var a6 = false && "Cat"; // f && t возвращает false
var a7 = "Cat" && false; // t && f возвращает false
```

Следующий код демонстрирует примеры использования оператора || (логическое ИЛИ).

```js
var o1 = true || true; // t || t возвращает true
var o2 = false || true; // f || t возвращает true
var o3 = true || false; // t || f возвращает true
var o4 = false || 3 == 4; // f || f возвращает false
var o5 = "Cat" || "Dog"; // t || t возвращает Cat
var o6 = false || "Cat"; // f || t возвращает Cat
var o7 = "Cat" || false; // t || f возвращает Cat
```

Следующий код демонстрирует примеры использования оператора ! (логическое НЕ).

```js
var n1 = !true; // !t возвращает false
var n2 = !false; // !f возвращает true
var n3 = !"Cat"; // !t возвращает false
```

#### Сокращённая оценка

Так как логические выражения вычисляются слева направо, они проверяются на возможность выполнения сокращённой оценки с использованием следующих правил:

* `false` && anything *-* сокращение с результатом false.
* `true` || *anything -* сокращение с результатом true.

Правила логики гарантируют, что данные вычисления всегда корректны. Обратите внимание, что часть "*anything"* представленных выше выражений не вычисляется, таким образом удаётся избежать любых побочных эффектов вычисления данной части.

### Строковые операторы

В дополнение к операторам сравнения, которые могут использоваться со строковыми значениями, оператор (+) позволяет объединить две строки, возвращая при этом третью строку, которая представляет собой объединение двух строк-операндов:

```js
console.log("my " + "string"); // в консоли выведется строка "my string".
```

Сокращённый оператор присваивания += также может быть использован для объединения (конкатенации) строк:

```js
var mystring = "alpha";
mystring += "bet"; // получается значение "alphabet" и присваивается mystring.
```

### Условный (тернарный) оператор

Условный оператор является единственным оператором JavaScript, который использует три операнда. Оператор принимает одно из двух значений в зависимости от заданного условия. Синтаксис оператора:
```

condition ? val1 : val2
```

**Предупреждение:** *val1 и val2 обязательно должны что-то возвращать, поэтому в этой конструкции нельзя использовать continue или break*

Если `condition (условие)` - истина, то оператор принимает значение `val1`. В противном случае оператор принимает значение `val2`. Вы можете использовать условный оператор во всех случаях, где может быть использован стандартный оператор.

```js
var status = age >= 18 ? "adult" : "minor";
```

Данное выражение присваивает значение "adult" переменной `status`, если `age` имеет значение 18 или более. В противном случае переменной `status` присваивается значение "minor".

### Оператор запятая

Оператор запятая (`,`) просто вычисляет оба операнда и возвращает значение последнего операнда. Данный оператор в основном используется внутри цикла `for`, что позволяет при каждом прохождении цикла одновременно обновлять значения нескольких переменных.

Например, если `a` является двумерным массивом, каждая строка которого содержит 10 элементов, то следующий код с использованием оператора запятая позволяет выполнять одновременное приращение двух переменных. Данный код выводит на экран значения диагональных элементов массива:

```js
for (var i = 0, j = 9; i <= 9; i++, j--)
  document.writeln("a[" + i + "][" + j + "]= " + a[i][j]);
```

### Унарные операторы

Унарная операция - операция только с одним операндом.

#### `delete`

Оператор delete выполняет удаление объекта, свойства объекта, или элемента массива с заданным индексом. Синтаксис оператора:

```js
delete objectName;
delete objectName.property;
delete objectName[index];
delete property; // допустимо только внутри with
```

где `objectName` представляет собой имя объекта, `property` - свойство объекта, а `index` - целое число, указывающее на положение (номер позиции) элемента в массиве.

Четвёртый вариант использования позволяет удалить свойство объекта, но допускается только внутри `with`.

Вы можете использовать оператор `delete` для удаления переменных, объявленных неявно, но вы не можете с его помощью удалять переменные, объявленные с помощью `var`.

После применения оператора `delete` свойство элемента меняется на `undefined`. Оператор `delete` возвращает `true` если выполнение операции возможно; оператор возвращает `false`, если выполнение операции невозможно.

```js
x = 42;
var y = 43;
myobj = new Number();
myobj.h = 4; // создаём свойство h
delete x; // возвращает true (можно удалить переменную объявленную неявно)
delete y; // возвращает false (нельзя удалить переменную объявленную с помощью var)
delete Math.PI; // возвращает false (нельзя удалить встроенные свойства)
delete myobj.h; // возвращает true (можно удалить пользовательские свойства)
delete myobj; // возвращает true (можно удалить объект объявленный неявно)
```

##### Удаление элементов массива

Удаление элемента массива не влияет на длину массива. Например, если вы удалите `a[3]`, элемент `a[4]` останется `a[4],` `a[3]` станет undefined.

Когда элемент массива удаляется с помощью оператора `delete`, то из массива удаляется значение данного элемента. В следующем примере элемент `trees[3]` удалён с помощью оператора `delete`. Однако, элемент `trees[3]` остаётся адресуемым и возвращает значение `undefined`.

```js
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
delete trees[3];
if (3 in trees) {
  // условие не выполняется
}
```

Если вы хотите, чтобы элемент оставался в массиве, но имел значение undefined, то используйте ключевое слово `undefined` вместо оператора `delete`. В следующем примере элементу `trees[3]` присвоено значение `undefined`, но элемент при этом остаётся в массиве:

```js
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
trees[3] = undefined;
if (3 in trees) {
  // данный блок кода выполняется
}
```

#### `Оператор typeof`

Оператор `typeof` используется одним из следующих способов:
```

typeof operand
typeof (operand)
```

Оператор `typeof` возвращает строку обозначающую тип невычисленного операнда. Значение `operand` может быть строкой, переменной, дескриптором, или объектом, тип которого следует определить. Скобки вокруг операнда необязательны.

Предположим, вы определяете следующие переменные:

```js
var myFun = new Function("5 + 2");
var shape = "round";
var size = 1;
var today = new Date();
```

Оператор `typeof` возвращает следующие результаты для этих переменных:

```js
typeof myFun; // возвращает "function"
typeof shape; // возвращает "string"
typeof size; // возвращает "number"
typeof today; // возвращает "object"
typeof dontExist; // возвращает "undefined"
```

Для дескрипторов `true` и `null` оператор `typeof` возвращает следующие результаты:

```js
typeof true; // возвращает "boolean"
typeof null; // возвращает "object"
```

Для чисел и строк оператор `typeof` возвращает следующие результаты:

```js
typeof 62; // возвращает "number"
typeof "Hello world"; // возвращает "string"
```

Для свойств оператор `typeof` возвращает тип значения данного свойства:

```js
typeof document.lastModified; // возвращает "string"
typeof window.length; // возвращает "number"
typeof Math.LN2; // возвращает "number"
```

Для методов и функций оператор `typeof` возвращает следующие результаты:

```js
typeof blur; // возвращает "function"
typeof eval; // возвращает "function"
typeof parseInt; // возвращает "function"
typeof shape.split; // возвращает "function"
```

Для встроенных объектов оператор `typeof` возвращает следующие результаты:

```js
typeof Date; // возвращает "function"
typeof Function; // возвращает "function"
typeof Math; // возвращает "object"
typeof Option; // возвращает "function"
typeof String; // возвращает "function"
```

#### `Оператор void`

Оператор `void` используется любым из следующих способов:
```

void (expression)
void expression
```

Оператор `void` определяет выражение, которое должно быть вычислено без возвращения результата. `expression` - это выражение JavaScript, требующее вычисления. Скобки вокруг выражения необязательны, но их использование является правилом хорошего тона.

Вы можете использовать оператор `void` для указания на то, что операнд-выражение является гипертекстовой ссылкой. При этом выражение обрабатывается, но не загружается в текущий документ.

Следующий код служит примером создания гипертекстовой ссылки, которая бездействует при нажатии на неё пользователем. Когда пользователь нажимает на ссылку, `void(0)` вычисляется равным `undefined`, что не приводит ни к каким действиям в JavaScript.

```html
<a href="javascript:void(0)">Нажмите здесь, чтобы ничего не произошло</a>
```

Приведённый ниже код создаёт гипертекстовую ссылку, которая подтверждает отправку формы при клике на ней пользователем:

```html
<a href="javascript:void(document.form.submit())">
  Нажмите здесь, чтобы подтвердить отправку формы</a
>
```

### Операторы отношения

Оператор отношения сравнивает свои операнды и возвращает результат сравнения в виде булева значения.

#### `Оператор in`

Оператор `in` возвращает true, если указанный объект имеет указанное свойство. Синтаксис оператора:
```

propNameOrNumber in objectName
```

где `propNameOrNumber` - строка или числовое выражение, представляющее имя свойства или индекс массива, а `objectName` - имя объекта.

Некоторые примеры способов использования оператора `in`:

```js
// Массивы
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees; // возвращает true
3 in trees; // возвращает true
6 in trees; // возвращает false
"bay" in trees; // возвращает false (следует указать индекс элемента массива,
// а не значение элемента)
"length" in trees; // возвращает true (length является свойством объекта Array)

// Встроенные объекты
"PI" in Math; // возвращает true
var myString = new String("coral");
"length" in myString; // возвращает true

// Пользовательские объекты
var mycar = { make: "Honda", model: "Accord", year: 1998 };
"make" in mycar; // возвращает true
"model" in mycar; // возвращает true
```

#### Оператор `instanceof`

Оператор `instanceof` возвращает true, если заданный объект является объектом указанного типа. Его синтаксис:
```

objectName instanceof objectType
```

где `objectName` - имя объекта, тип которого необходимо сравнить с `objectType`, а `objectType` - тип объекта, например, `Date` или `Array`.

`Используйте оператор instanceof`, когда вам необходимо подтвердить тип объекта во время выполнения программы. Например, при перехвате исключений вы можете создать различные программные переходы для обработки исключений в зависимости от типа обрабатываемого исключения.

Например, следующий код использует оператор `instanceof` для проверки того, является ли объект `theDay` объектом типа `Date`. Так как `theDay` действительно является объектом типа `Date`, то программа выполняет код, содержащийся в утверждении `if`.

```js
var theDay = new Date(1995, 12, 17);
if (theDay instanceof Date) {
  // выполняемый код
}
```

### Приоритет операторов

*Приоритет* операторов определяет порядок их выполнения при вычислении выражения. Вы можете влиять на приоритет операторов с помощью скобок.

Приведённая ниже таблица описывает приоритет операторов от наивысшего до низшего.

| Тип оператора | Операторы |
| --- | --- |
| свойство объекта | `. []` |
| вызов, создание экземпляра объекта | `() new` |
| отрицание, инкремент | `! ~ - + ++ -- typeof void delete` |
| умножение, деление | `* / %` |
| сложение, вычитание | `+ -` |
| побитовый сдвиг | `<< >> >>>` |
| сравнение, вхождение | `< <= > >= in instanceof` |
| равенство | `== != === !==` |
| битовое-и | `&` |
| битовое-исключающее-или | `^` |
| битовое-или | `|` |
| логическое-и | `&&` |
| логическое-или | `||` |
| условный (тернарный) оператор | `?:` |
| присваивание | `= += -= *= /= %= <<= >>= >>>= &= ^= |=` |
| запятая | `,` |

Более подробная версия данной таблицы, содержащая ссылки и дополнительную информацию по каждому оператору, находится в справочнике JavaScript.

Выражения
---------

*Выражением* является любой корректный блок кода, который возвращает значение.

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

Выражение `x = 7` является примером выражения первого типа. Данное выражение использует *оператор* = для присваивания переменной `x` значения 7. Само выражение также равняется 7.

Код `3 + 4` является примером выражения второго типа. Данное выражение использует *оператор* "+" для сложения чисел 3 и 4 без присваивания переменной полученного результата 7.

Все выражения в JavaScript делятся на следующие категории:

* **Арифметические**: вычисляются в число, например: 3.14159 (Используют арифметические операторы).
* **Строковые**: вычисляются в текстовую строку, например: "Fred" или "234" (Используют строковые операторы).
* **Логические**: вычисляются в true или false (Используют логические операторы).
* **Основные выражения**: Базовые ключевые слова и основные выражения в JavaScript.
* **Левосторонние выражения**: Значениям слева назначаются значения справа.
### Основные выражения

Базовые ключевые слова и основные выражения в JavaScript.

#### Оператор `this`

Используйте ключевое слово `this` для указания на текущий объект. В общем случае `this` указывает на вызываемый объект, которому принадлежит данный метод. Используйте `this` следующим образом:
```

this["propertyName"]
this.propertyName
```

Предположим, функция `validate` выполняет проверку свойства `value` некоторого объекта; задан объект, а также верхняя и нижняя граница величины данного свойства:

```js
function validate(obj, lowval, hival) {
  if (obj.value < lowval || obj.value > hival) alert("Неверное значение!");
}
```

Вы можете вызвать функцию `validate` для обработчика события `onChange` для каждого элемента формы, используя `this` для указания на элемент формы, как это показано в следующем примере:

```html
<b>Введите число от 18 до 99:</b>
<input type="text" name="age" size="3" onChange="validate(this, 18, 99);" />
```

#### Оператор группировки

Оператор группировки `"скобки" ( )` контролирует приоритет вычислений в выражениях. Например, вы можете переопределить порядок так, чтобы сложение выполнялось до умножения:

```js
var a = 1;
var b = 2;
var c = 3;

// обычный порядок
a + b \* c; // 7
// по умолчанию выполняется так
a + (b \* c); // 7

// теперь поменяем приоритет с помощью скобок,
// чтобы сложение выполнялось до умножения
(a + b) \* c; // 9

// что эквивалентно следующему
a \* c + b \* c; // 9
```

#### Упрощённый синтаксис создания массивов и генераторов

Упрощённый синтаксис - экспериментальная возможность JavaScript, которая возможно будет добавлена в будущие версии ECMAScript. Есть 2 версии синтаксиса:

Экспериментальная возможность `[for (x of y) x]` (en-US)

Упрощённый синтаксис для массивов.

Экспериментальная возможность `(for (x of y) y)` (en-US)

Упрощённый синтаксис для генераторов.

Упрощённые синтаксисы существуют во многих языках программирования и позволяют вам быстро собирать новый массив, основанный на существующем. Например:

```js
[for (i of [ 1, 2, 3 ]) i\*i ];
// [ 1, 4, 9 ]

var abc = [ "A", "B", "C" ];
[for (letters of abc) letters.toLowerCase()];
// [ "a", "b", "c" ]
```

### Левосторонние выражения

Значениям слева назначаются значения справа.

#### `new`

Вы можете использовать оператор `new` для создания экземпляра объекта пользовательского типа или одного из встроенных объектов. Используйте оператор `new` следующим образом:

```js
var objectName = new objectType([param1, param2, ..., paramN]);
```

#### super

Ключевое слово используется, чтобы вызывать функции родительского объекта. Это полезно и с классами для вызова конструктора родителя, например.

```js
super([arguments]); // вызывает конструктор родителя. super.functionOnParent([arguments]);
```

#### Оператор расширения

Оператор расширения позволяет выражению расширяться в местах с множеством аргументов (для вызовов функций) или множестве элементов (для массивов).

**Пример:** Сегодня, если у вас есть массив и вы хотите создать новый с существующей частью первого, то литерального синтаксиса массива уже не достаточно, и вы должны писать императивный (без вариантов) код, используя комбинацию `push`, `splice`, `concat` и т.д. Но с этим оператором код становится более коротким:

```js
var parts = ["shoulder", "knees"];
var lyrics = ["head", ...parts, "and", "toes"];
```

Похожим образом оператор работает с вызовами функций:

```js
function f(x, y, z) {}
var args = [0, 1, 2];
f(...args);
```

* « Предыдущая статья
* Следующая статья »


Регулярные выражения - JavaScript

Регулярные выражения
====================

* « Предыдущая статья
* Следующая статья »

Регулярные выражения - это шаблоны, используемые для сопоставления последовательностей символов в строках. В JavaScript регулярные выражения также являются объектами. Эти шаблоны используются в методах `exec` (en-US) и `test` (en-US) объекта `RegExp` (en-US) а также `match` (en-US), `replace`, `search` (en-US), `split` (en-US) объекта `String`. Данная глава описывает регулярные выражения в JavaScript.

Создание регулярного выражения
------------------------------

Регулярное выражение можно создать двумя способами:

* Используя литерал регулярного выражения, например:

```js
var re = /ab+c/;
```

Литералы регулярных выражений вызывают предварительную компиляцию регулярного выражения при анализе скрипта. Если ваше регулярное выражение постоянно, то пользуйтесь им, чтобы увеличить производительность.
* Вызывая функцию конструктор объекта `RegExp` (en-US), например:

```js
var re = new RegExp("ab+c");
```

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

Шаблон регулярного выражения состоит из обычных символов, например `/abc/`, или комбинаций обычных и специальных символов, например `/ab*c/` или `/Chapter (\d+)\.\d*/`. Последний пример включает в себя скобки, которые используются как "запоминающий механизм". Соответствие этой части шаблона запоминается для дальнейшего использования, как описано в Использование совпадений подстрок заключённых в скобки.

### Использование простых шаблонов

Простые шаблоны используются для нахождения прямого соответствия в тексте. Например, шаблон `/abc/` соответствует комбинации символов в строке только когда символы 'abc' встречаются вместе и в том же порядке. Такое сопоставление произойдёт в строке "Hi, do you know your abc's?" и "The latest airplane designs evolved from slabcraft." В обоих случаях сопоставление произойдёт с подстрокой 'abc'. Сопоставление не произойдёт в строке "Grab crab", потому что она не содержит подстроку 'abc'.

### Использование специальных символов

В случае когда поиск соответствия требует чего-то большего, чем прямое сопоставление, например нахождение последовательности символов 'b' или нахождение пробела, шаблон включает в себя специальные символы. Например, шаблон `/ab*c/` соответствует любой комбинации символов, в которой за 'a' следует ноль или более символов 'b' (`*` означает ноль или более вхождений предыдущего символа), за которыми сразу же следует символ 'c'. В строке "cbbabbbbcdebc," этому шаблону сопоставляется подстрока 'abbbbc'.

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

Таблица 4.1 Специальные символы в регулярных выражения.| Символ | Значение |
| --- | --- |
| `\` | Одно из следующего:
 * Для символов обычно обрабатываемых буквально, означает что следующий
 символ является специальным и не должен интерпретироваться
 буквально.
* Например, `/b/` сопоставляется символу 'b'. Добавляя слеш
 перед b, т.е используя `/\b/`, символ становится
 специальным символом, означающим границу слова.
* Для символов обычно обрабатываемых особым образом означает, что
 следующий символ не является специальным и должен интерпретироваться
 буквально.
* Например, `*` является специальным символом,
 сопоставляемым 0 или более повторений предыдущего символа; например,
 `/a*/` означает соответствие 0 или более символов а. Для
 буквальной интерпретации \*, поставьте перед ней обратный слеш;
 например, `/a\*/` соответствует 'a\*'.
* Также не забудьте заэкранировать сам \ при его использовании в
 записи new RegExp("pattern") поскольку \ также является экранирующим
 символом в обычных строках.
 |
| `^` |
 Соответствует началу ввода. Если установлен флаг многострочности,
 также производит сопоставление непосредственно после переноса строки.

 Например, `/^A/` не соответствует 'A' в "an A", но
 соответствует 'A' в "An E".

Этот символ имеет другое значение при появлении в начале шаблона
 набора символов.

 Например, `/[^a-z\s]/` соответствует 'I' в "I have 3
 sisters".
  |
| `$` |
 Соответствует концу ввода. Если установлен битовый флаг
 многострочности, также сопоставляется содержимому до переноса строки.

 Например, `/t$/` не соответствует 't' в строке "eater", но
 соответствует строке "eat".
  |
| `*` |
 Соответствует предыдущему символу повторенному 0 или более раз.
 Эквивалентно {0,}.

 Например, `/bo*/` соответствует 'boooo' в "A ghost booooed"
 и 'b' в "A bird warbled", но не в "A goat grunted".
  |
| `+` |
 Соответствует предыдущему символу повторенному 1 или более раз.
 Эквивалентно {1,}.

 Например, `/a+/` соответствует 'a' в "candy" и всем
 символам 'a' в "caaaaaaandy".
  |
| `?` | Соответствует предыдущему символу повторенному
 0 или 1 раз. Эквивалентно {0,1}.

 Например, `/e?le?/` соответствует 'el' в "angel" и 'le' в
 "angle" а также 'l' в "oslo".

 Если использован сразу после квалификаторов `*`,
 `+`, `?`, или `{}`, делает
 квалификатор "нежадным" (соответствующим минимальному количеству
 символов), в отличие от режима по умолчанию, являющимся "жадным"
 (соответствующим максимальному числу символов). Например, используя
 /\d+/ не глобальное сопоставление "123abc" возвращает "123", если
 использовать /\d+?/, только "1" будет возвращена.

 Также используется в упреждающих утверждениях (assertions), описанных
 в строках x(?=y) и x(?!y) данной таблицы.
  |
| `.` | (десятичная точка) соответствует любому символу кроме переноса строки.

 Например, `/.n/` соответствует 'an' и 'on' в "nay, an apple
 is on the tree", но не 'nay'.
  |
| `(x)` |
 Соответствует 'x' и запоминает это соответствие. Это называется
 захватывающие скобки.

 Например, `/(foo)/` соответствует 'foo' в "foo bar."
 Сопоставленная строка может быть получена из элементов результирующего
 массива `[1]`, ..., `[n]`.
  |
| `(?:x)` |
 Соответствует 'x' но не запоминает соответствие. Это называется
 не-захватывающие скобки. Сопоставленная строка не может быть получена из
 элементов результирующего массива
 `[1]`, ...,
 `[n]`.
  |
| `x(?=y)` |
 Соответствует 'x' только если за 'x' следует 'y'. Это называется
 упреждение.

 Например, `/Jack(?=Sprat)/` соответствует 'Jack' только
 если за ним следует 'Sprat'.
 `/Jack(?=Sprat|Frost)/` соответствует 'Jack' только если за
 ним следует 'Sprat' или 'Frost'. Тем не менее, ни 'Sprat' ни 'Frost'
 не являются частью сопоставленного результата.
  |
| `x(?!y)` |
 Соответствует 'x' только если за 'x' не следует 'y'. Это называется
 отрицательное упреждение.

 Например, `/\d+(?!\.)/` соответствует числу только если за
 ним не следует десятичная точка. Регулярное выражение
 `/\d+(?!\.)/.exec("3.141")` сопоставит '141' но не '3.141'.
  |
| `x|y` | Соответствует либо 'x' либо 'y'.

 Например, `/green|red/` соответствует 'green' в "green
 apple" и 'red' в "red apple."
  |
| `{n}` |
 n - положительное целое. Соответствует ровно `n` вхождениям
 предыдущего символа.

 Например, `/a{2}/` не соответствует 'a' в "candy," но
 соответствует всем а в "caandy," первым двум а в "caaandy."
  |
| `{n,m}` |
 m и n - положительные целые. Соответствует как минимум
 `n` и максимум `m` вхождениям предыдущего
 символа. При m=n=1 пропускается.

 Например, `/a{1,3}/` ничему не соответствует в строке
 "cndy", символу 'a' в "candy," двум а в "caandy," и трём первым а в
 "caaaaaaandy". Отметим, что при сопоставлении "caaaaaaandy", совпадает
 "aaa", хотя изначальная строка содержит больше а.
  |
| `[xyz]` |
 Набор символов. Соответствует любому символу из перечисленных. Можно
 указать диапазон символов, используя тире. Специальные символы (как
 точка (`.`) и звёздочка (`*`)) не имеют
 специального значения внутри такого набора. Их не надо экранировать.
 Экранирование работает также.

 Например, `[abcd]` эквивалентна
 [`a-d]`. Они
 соответствуют 'b' в "brisket" и 'c' в "city". `/[a-z.]+/` и
 `/[\w.]+/` обе соответствуют всему в "test.i.ng".
  |
| `[^xyz]` |
 Инвертированный или дополняющий набор символов. Это означает
 соответствие всему, что не в скобках. Можно указать диапазон символов
 с помощью тире. Все, что действует в обычном наборе символов,
 действует и здесь.

 Например, `[^abc]` эквивалентно `[^a-c]`. Они
 соответствуют изначально 'r' в "brisket" и 'h' в "chop."
  |
| `[\b]` | Соответствует бэкспейсу (U+0008). (Не путать с `\b`.) |
| `\b` |
 Соответствует границе слова. Граница слова соответствует позиции, где
 за символом слова не следует другой символ слова или предшествует ему.
 Отметим, что граница слова не включается в соответствие. Другими
 словами, длина сопоставленной границы слова равна нулю. (Не путать с
 `[\b]`.)

 Примеры:`/\bmoo/` соответствует 'moo' в слове "moon"
 ;`/oo\b/` не соответствует 'oo' в слове "moon",
 поскольку за 'oo' следует символ 'n' , являющимся символом слова;`/oon\b/`
 соответствует 'oon' в слове "moon", поскольку 'oon' является
 окончанием строки, и таким образом, за этими символами не следует
 другой символ слова;`/\w\b\w/` никогда не будет
 ничему соответствовать, поскольку за символом слова никогда не может
 следовать и граница слова, и символ слова.

**Note:** JavaScript's regular expression engine
 defines a
 specific set of charactersto be "word" characters. Any character not in that set is
 considered a word break. This set of characters is fairly limited:
 it consists solely of the Roman alphabet in both upper- and
 lower-case, decimal digits, and the underscore character. Accented
 characters, such as "é" or "ü" are, unfortunately, treated as word
 breaks.

 |
| `\B` |
 Соответствует несловообразующей границе. Несловообразующая граница
 соответствует позиции, в которой предыдущий и следующий символы
 являются символами одного типа: либо оба должны быть словообразующими
 символами, либо несловообразующими. Начало и конец строки считаются
 несловообразующими символами.

 Например, `/\B../` соответствует 'oo' в слове "noonday" (,
 а `/y\B./` соответствует 'ye' в "possibly yesterday."
  |
| `\c*X*` |
 Где *X* является символом случайного выбора из
 последовательности от А до Я. Соответствует управляющему символу в
 строке.

 Например, `/\cM/` соответствует control-M (U+000D) в
 строке.
  |
| `\d` |
 Соответствует цифровому символу. Эквивалентно выражению
 `[0-9]`.

 Например, `/\d/` or `/[0-9]/` соответствует '2'
 в "B2 is the suite number."
  |
| `\D` |
 Соответствует любому нецифровому символу. Эквивалентно выражению
 `[^0-9]`.

 Например, `/\D/` or `/[^0-9]/` соответствует 'B'
 в предложении "B2 is the suite number."
  |
| `\f` |
 Соответствует символу прогона страницы (U+000C). Особый символ
 управления печатью.
  |
| `\n` | Соответствует символу перевода строки (U+000A). |
| `\r` | Соответствует символу возврата каретки (U+000D). |
| `\s` |
 Соответствует одиночному символу пустого пространства, включая пробел,
 табуляция, прогон страницы, перевод строки. Эквивалентен
 `[
\f\n\r\t\v\u00A0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u2028\u2029\u202f\u205f\u3000]`.

Например, `/\s\w*/` совпадает с ' bar' в "foo bar." |
| `\S` |
 Соответствует одиночному символу непустого пространства.
 Эквивалентен`[^
\f\n\r\t\v\u00A0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]`.

Например, `/\S\w*/` совпадает с 'foo' в "foo bar." |
| `\t` | Соответствует символу горизонтальной табуляции (U+0009). |
| `\v` | Соответствует символу вертикальной табуляции (U+000B). |
| `\w` |
 Соответствует любому цифробуквенному символу включая нижнее
 подчёркивание. Эквивалентен `[A-Za-z0-9_]`.

 Например, `/\w/` совпадает с 'a' в "apple," '5' в "$5.28,"
 и '3' в "3D."
  |
| `\W` | Соответствует любому не цифробуквенному символу. Равносилен`[^A-Za-z0-9_]`.

 Например, `/\W/` or `/[^A-Za-z0-9_]/` совпадает
 с '%' в "50%."
  |
| `\*n*` |
 Где *n* это положительное целое, обратная ссылка на последнюю
 найденную подстроку, соответствующую *n* , заключённую в
 круглые скобки в регулярном выражении (считая левые скобки).

 Например, `/apple(,)\sorange\1/` соответствует 'apple,
 orange,' в "apple, orange, cherry, peach."
  |
| `\0` |
 Соответствует символу NULL (U+0000). Не следует ставить за ним другой
 цифровой символ, поскольку `\0<digits>` является
 восьмеричной экранирующей последовательностью.
  |
| `\xhh` | Соответствует символам кода hh (две шестнадцатеричные цифры) |
| `\uhhhh` | Соответствует символам кода hhhh (четыре шестнадцатеричные цифры). |

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

```js
function escapeRegExp(string) {
  return string.replace(/[.\*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}
```

### Использование скобок

Скобки вокруг любой части регулярного выражения означают что эта часть совпадаемой подстроки будет запомнена. Раз запомнена, подстрока может выбрана для использования как это описано в Using Parenthesized Substring Matches.

Например, паттерн `/Chapter (\d+)\.\d*/` включает в себя дополнительные экранирующие и специальные символы и указывает на то, что часть шаблона должна быть запомнена. Он точно соответствует символам слова 'Chapter ', за которыми следует один или более цифровых символов (`\d` означает любой цифровой символ, а '`+'` означает 1 или более раз), за которым следует десятичная точка (сама по себе являющаяся специальным символом; предшествующий десятичной точке слеш ' \' означает, что паттерн должен искать литеральный символ '.'), после которой следует любой цифровой символ 0 или более раз ('`\d'` обозначает цифровой символ, '`*'` обозначает 0 или более раз). Кроме того, круглые скобки используются для запоминания первых же совпавших цифровых символов.

Этот шаблон будет найден во фразе "Open Chapter 4.3, paragraph 6" и цифра '4' будет запомнена. Но он не будет найден во фразе "Chapter 3 and 4", поскольку эта строка не имеет точки после цифры '3'.

Для того, чтобы сопоставить подстроку без вызова совпавшей части для запоминания, внутри круглых скобок необходимо предварить паттерн сочетанием символов ' `?:'`. Например, шаблон `(?:\d+)` будет соответствовать одному или более цифровому символу, но не запомнит совпавших символов.

Работа с Регулярными Выражениями
--------------------------------

Регулярные выражения используются в методах `test` и `exec` объекта `RegExp` и с методами `match`, `replace`, `search`, и `split` объекта `String`. Эти методы подробно объясняются в Справочнике JavaScript

| Метод | Описание |
| --- | --- |
| `exec` (en-US) | Метод `RegExp,` который выполняет поиск совпадения в строке. Он возвращает массив данных. |
| `test` (en-US) | Метод `RegExp`, который тестирует совпадение в строке. Возвращает либо истину либо ложь. |
| `match` (en-US) | Метод `String`, который выполняет поиск совпадения в строке. Он возвращает массив данных либо null если совпадения отсутствуют. |
| `search` (en-US) | Метод `String,` который тестирует на совпадение в строке. Он возвращает индекс совпадения, или -1 если совпадений не будет найдено. |
| `replace` | Метод `String`, который выполняет поиск совпадения в строке, и заменяет совпавшую подстроку другой подстрокой, переданной как аргумент в этот метод. |
| `split` (en-US) | Метод `String,` который использует регулярное выражение или фиксированную строку чтобы разбить строку на массив подстрок. |

Чтобы просто узнать есть ли в строке что либо соответствующее шаблону, воспользуйтесь методами `test` или `search`; а чтобы получить больше информации пользуйтесь методами `exec` или `match` (хотя эти методы работают медленнее). Если вы пользуетесь `exec` или `match` и если совпадения есть, эти методы вернут массив и обновлённые свойства объекта ассоциированного регулярного выражения а также предопределённого объекта `RegExp` регулярного выражения. Если совпадений нет, метод `exec` вернёт `null` (который сконвертируется в `false`).

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

```js
var myRe = /d(b+)d/g;
var myArray = myRe.exec("cdbbdbsbz");
```

Если вам не нужен доступ к свойствам регулярного выражения, то альтернативный способ получить `myArray` можно так:

```js
var myArray = /d(b+)d/g.exec("cdbbdbsbz");
```

Если вы хотите сконструировать регулярное выражение из строки, другой способ сделать это приведён ниже:

```js
var myRe = new RegExp("d(b+)d", "g");
var myArray = myRe.exec("cdbbdbsbz");
```

С помощью этих скриптов, поиск совпадения завершается и возвращает массив и обновлённые свойства показанные в след. таблице.

Table 4.3 Результаты выполнения регулярного выражения| Объект | Свойство или индекс | Описание | В этом примере. |
| --- | --- | --- | --- |
| `myArray` |  | Совпавшая строка и все запомненные подстроки. | `["dbbd", "bb"]` |
| `index` | Индекс совпавшей подстроки (индекс начинается с нуля). | `1` |
| `input` | Исходная строка. | `"cdbbdbsbz"` |
| `[0]` | Последние совпавшие символы. | `"dbbd"` |
| `myRe` | `lastIndex` |
 Индекс с которого начнётся след. поиск совпадения. (Это свойство
 определяется только если регулярное выражение использует параметр g,
 описанный в
 [Advanced Searching With Flags](#Advanced\_Searching\_With\_Flags).)
  | `5` |
| `source` |
 Текст шаблона. Обновляется в момент создания регулярного выражения, а не
 во время выполнения.
  | `"d(b+)d"` |

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

```js
var myRe = /d(b+)d/g;
var myArray = myRe.exec("cdbbdbsbz");
console.log("The value of lastIndex is " + myRe.lastIndex);
```

Этот скрипт выведет:
```

The value of lastIndex is 5
```

Однако, если у вас есть следующий скрипт:

```js
var myArray = /d(b+)d/g.exec("cdbbdbsbz");
console.log("The value of lastIndex is " + /d(b+)d/g.lastIndex);
```

Он выведет:
```

The value of lastIndex is 0
```

Совпадения `/d(b+)d/g` в двух случаях являются разными объектами регулярного выражения и, следовательно, имеют различные значения для свойства `lastIndex`. Если вам необходим доступ к свойствам объекта, созданного при помощи инициализатора, то вы должны сначала присвоить его переменной.

### Использование скобочных выражений для нахождения подстрок

Использование скобок в шаблоне регулярного выражения повлечёт "запоминание" совпавшей подстроки. Для примера, `/a(b)c/` вызовет совпадение 'abc' и запомнит 'b'. Чтобы получить совпадения скобочного выражения используйте `Array` elements `[1]`, ..., `[n]`.

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

Следующий скрипт использует метод `replace(),` чтобы поменять местами слова (символы) в строке. Для замены текста скрипт использует обозначения `$1` и `$2` для обозначения первого и второго совпадения скобочного выражения.

```js
var re = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(re, "$2, $1");
console.log(newstr);
```

Выведет "Smith, John".

### Расширенный поиск с флагами

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

| Flag | Description |
| --- | --- |
| `g` | Глобальный поиск. |
| i | Регистронезависимый поиск. |
| m | Многострочный поиск. |
| y | Выполняет поиск начиная с символа, который находится на позиции свойства lastindex текущего регулярного выражения. |

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

```js
var re = /pattern/flags;
```

или

```js
var re = new RegExp("pattern", "flags");
```

Обратите внимание, что флаги являются неотъемлемой частью регулярного выражения. Флаги не могут быть добавлены или удалены позднее.

Для примера, `re = /\w+\s/g` создаёт регулярное выражение, которое ищет один или более символов, после которых следует пробел и ищет данное совпадение на протяжении всей строки.

```js
var re = /\w+\s/g;
var str = "fee fi fo fum";
var myArray = str.match(re);
console.log(myArray);
```

Выведет ["fee ", "fi ", "fo "]. В этом примере вы бы могли заменить строку:

```js
var re = /\w+\s/g;
```

на следующую:

```js
var re = new RegExp("\\w+\\s", "g");
```

и получить тот же результат.

Флаг `m` используется, чтобы входная строка рассматривалась как многострочная. Если флаг `m` используется, то `^` и `$` вызовет совпадение в начале или конце любой строки в строке ввода вместо начала или конца вводимой строки целиком.

Примеры
-------

След. примеры показывают использование регулярных выражений.

### Изменение порядка в Исходной Строке

След. пример иллюстрирует формирование регулярного выражения и использование `string.split()` и `string.replace()`. Он очищает неправильно сформатированную исходную строку, которая содержит имена в неправильном порядке (имя идёт первым) разделённые пробелами, табуляцией и одной точкой с запятой. В конце, изменяется порядок следования имён (фамилия станет первой) и сортируется список.

```js
// The name string contains multiple spaces and tabs,
// and may have multiple spaces between first and last names.
var names = "Harry Trump ;Fred Barney; Helen Rigby ; Bill Abel ; Chris Hand ";

var output = ["---------- Original String\n", names + "\n"];

// Prepare two regular expression patterns and array storage.
// Split the string into array elements.

// pattern: possible white space then semicolon then possible white space
var pattern = /\s\*;\s\*/;

// Break the string into pieces separated by the pattern above and
// store the pieces in an array called nameList
var nameList = names.split(pattern);

// new pattern: one or more characters then spaces then characters.
// Use parentheses to "memorize" portions of the pattern.
// The memorized portions are referred to later.
pattern = /(\w+)\s+(\w+)/;

// New array for holding names being processed.
var bySurnameList = [];

// Display the name array and populate the new array
// with comma-separated names, last first.
//
// The replace method removes anything matching the pattern
// and replaces it with the memorized string—second memorized portion
// followed by comma space followed by first memorized portion.
//
// The variables $1 and $2 refer to the portions
// memorized while matching the pattern.

output.push("---------- After Split by Regular Expression");

var i, len;
for (i = 0, len = nameList.length; i < len; i++) {
  output.push(nameList[i]);
  bySurnameList[i] = nameList[i].replace(pattern, "$2, $1");
}

// Display the new array.
output.push("---------- Names Reversed");
for (i = 0, len = bySurnameList.length; i < len; i++) {
  output.push(bySurnameList[i]);
}

// Sort by last name, then display the sorted array.
bySurnameList.sort();
output.push("---------- Sorted");
for (i = 0, len = bySurnameList.length; i < len; i++) {
  output.push(bySurnameList[i]);
}

output.push("---------- End");

console.log(output.join("\n"));
```

### Использование спецсимволов для проверки входных данных

В след. примере, ожидается что пользователь введёт телефонный номер и требуется проверить правильность символов набранных пользователем. Когда пользователь нажмёт кнопку "Check", скрипт проверит правильность введённого номера. Если номер правильный (совпадает с символами определёнными в регулярном выражении), то скрипт покажет сообщение благодарности для пользователя и подтвердит номер. Если нет, то скрипт проинформирует пользователя, что телефонный номер неправильный.

Внутри незахватывающих скобок `(?:`, регуляное выражение ищет три цифры `\d{3}` ИЛИ `|` открывающую скобку `\(`, затем три цифры `\d{3}`, затем закрывающую скобку `\)`, (закрывающая незахватывающая скобка `)`), затем тире, слеш, или десятичная точка, и когда это выражение найдено, запоминает символ `([-\/\.])`, следующие за ним и запомненные три цифры `\d{3}`, следующее соответствие тире, слеша или десятичной точки `\1`, и следующие четыре цифры `\d{4}`.

Регулярное выражение ищет сначала 0 или одну открывающую скобку `\(?`, затем три цифры `\d{3}`, затем 0 или одну закрывающую скобку `\)?`, потом одно тире, слеш или точка и когда найдёт это, запомнит символ `([-\/\.])`, след. три цифры `\d{3}`, followed by the remembered match of a dash, forward slash, or decimal point `\1`, followed by four digits `\d{4}`.

Событие "Изменить" активируется, когда пользователь подтвердит ввод значения регулярного выражения, нажав клавишу "Enter".

```html
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
    <meta http-equiv="Content-Script-Type" content="text/javascript" />
    <script type="text/javascript">
 var re = /\(?\d{3}\)?([-\/\.])\d{3}\1\d{4}/;
 function testInfo(phoneInput) {
 var OK = re.exec(phoneInput.value);
 if (!OK)
 window.alert(RegExp.input + " isn't a phone number with area code!");
 else window.alert("Thanks, your phone number is " + OK[0]);
 }
 </script>
  </head>
  <body>
    <p>
      Enter your phone number (with area code) and then click "Check". <br />The
      expected format is like ###-###-####.
    </p>
    <form action="#">
      <input id="phone" /><button
 onclick="testInfo(document.getElementById('phone'));">
        Check
      </button>
    </form>
  </body>
</html>
```


Мета-программирование - JavaScript

Мета-программирование
=====================

* « Предыдущая статья

С приходом ECMAScript 2015, в JavaScript введены объекты `Proxy` и `Reflect`, позволяющие перехватить и переопределить поведение фундаментальных процессов языка (таких как поиск свойств, присвоение, итерирование, вызов функций и так далее). С помощью этих двух объектов вы можете программировать на мета уровне JavaScript.

Объекты Proxy
-------------

Введённый в ECMAScript 6, объект `Proxy` позволяет перехватить и определить пользовательское поведение для определённых операций. Например, получение свойства объекта:

```js
var handler = {
  get: function (target, name) {
    return name in target ? target[name] : 42;
  },
};
var p = new Proxy({}, handler);
p.a = 1;
console.log(p.a, p.b); // 1, 42
```

Объект `Proxy` определяет `target` (в данном случае новый пустой объект) и `handler` - объект в котором реализована особая *функция-ловушка* `get`. "Проксированный" таким образом объект, при доступе к его несуществующему свойству вернёт не `undefined,` а числовое значение 42.

Дополнительные примеры доступны в справочнике `Proxy`.

### Терминология

В разговоре о функциях объекта `Proxy` применимы следующие термины:

handler (en-US) (обработчик)

Объект - обёртка, содержащий в себе функции-ловушки.

ловушки (traps)

Методы, реализующие доступ к свойствам. В своей концепции они аналогичны методам перехвата(hooking) в операционных системах.

цель (target)

Объект, который оборачивается в Proxy. Часто используется лишь как внутреннее хранилище для Proxy объекта. Проверка на нарушение ограничений (invariants), связанных с нерасширяемостью объекта или неконфигурируемыми свойствами объекта производится для конкретной **цели**.

неизменяемые ограничения (дословно Invariants - те что остаются неизменными)

Некоторые особенности поведения объекта, которые должны быть сохранены при реализации пользовательского поведения названы invariants. Если в обработчике нарушены такие ограничения, будет выброшена ошибка `TypeError`.

Обработчики и ловушки
---------------------

В следующей таблице перечислены ловушки, доступные для использования в объекте `Proxy`. Смотрите подробные объяснения и примеры в документации (en-US).

| Обработчик / ловушка | Перехватываемые методы | Неизменяемые ограничения |
| --- | --- | --- |
| `handler.getPrototypeOf()` (en-US) | `Object.getPrototypeOf()``Reflect.getPrototypeOf()` (en-US)`__proto__``Object.prototype.isPrototypeOf()``instanceof` | * `метод getPrototypeOf` должен вернуть object или
 `null`.
* если целевой объект `target` нерасширяем, метод
 `Object.getPrototypeOf(proxy)` должен возвращать тот же
 результат что и `Object.getPrototypeOf(target)`.
 |
| `handler.setPrototypeOf()` (en-US) | `Object.setPrototypeOf()``Reflect.setPrototypeOf()` (en-US) |
 если целевой объект `target` нерасширяем, значение параметра
 `prototype` должно быть равным значению возвращаемому методом
 `Object.getPrototypeOf(target)`.
  |
| `handler.isExtensible()` (en-US) | `Object.isExtensible()``Reflect.isExtensible()` (en-US) | `Object.isExtensible(proxy)` должно возвращать тоже значение,
 что и `Object.isExtensible(target)`.
  |
| `handler.preventExtensions()` (en-US) | `Object.preventExtensions()``Reflect.preventExtensions()` (en-US) | `Object.preventExtensions(proxy)` возвращает
 `true` только в том случае, если
 `Object.isExtensible(proxy)` равно `false`.
  |
| `handler.getOwnPropertyDescriptor()` (en-US) | `Object.getOwnPropertyDescriptor()``Reflect.getOwnPropertyDescriptor()` (en-US) | * `метод getOwnPropertyDescriptor` должен возвращать object
 или `undefined`.
* Свойство не может быть описано как несуществующее, если оно
 существует и является некофигурируемым, собственным свойством
 целевого объекта `target`.
* Свойство не может быть описано как несуществующее, если оно
 существует как собственное свойство целевого объекта
 `target` и `target` не расширяем.
* Свойство не может быть описано как существующее, если оно не
 существует как собственное свойство целевого объекта
 `target` и `target` не расширяем.
* Свойство не может быть описано как неизменяемое, если оно не
 существует как собственное свойство целевого объекта
 `target` или если оно существует и является изменяемым,
 собственным свойством целевого объекта `target`.
* Значение возвращённое методом
 `Object.getOwnPropertyDescriptor(target)` может быть
 применено к целевому объекту через метод
 `Object.defineProperty и э`то не вызовет ошибки.
 |
| `handler.defineProperty()` (en-US) | `Object.defineProperty()``Reflect.defineProperty()` | * Новое свойство не может быть добавлено, если целевой объект не
 расширяем.
* Нельзя добавить новое конфигурируемое свойство, или преобразовать
 существующее свойство в конфигурируемое, если оно не существует как
 собственное свойство целевого объекта или не является
 конфигурируемым.
* Свойство не может быть неконфигурируемым, если целевой объект имеет
 соответствующее собственное, конфигурируемое свойство.
* Если объект имеет свойство соответствующее создаваемому свойству, то
 `Object.defineProperty(target, prop, descriptor)` не
 вызовет ошибки.
* В строгом режиме ("use strict";), если обработчик
 `defineProperty` вернёт `false`, это вызовет
 ошибку `TypeError`.
 |
| `handler.has()` (en-US) |
 Property query: `foo in proxy`Inherited property query:
 `foo in Object.create(proxy)``Reflect.has()` (en-US) | * Свойство не может быть описано как несуществующее, если оно
 существует как собственное неконфигурируемое свойство целевого
 объекта.
* Свойство не может быть описано как несуществующее, если оно
 существует как собственное свойство целевого объекта, и целевой
 объект является нерасширяемым.
 |
| `handler.get()` (en-US) | Property access: `proxy[foo]`and `proxy.bar`Inherited property access: `Object.create(proxy)[foo]``Reflect.get()` | * Значение, возвращаемое для свойства, должно равняться значению
 соответствующего свойства целевого объекта, если это свойство
 является доступным только для чтения, неконфигурируемым.
* Значение, возвращаемое для свойства, должно равняться
 `undefined,` если соответствующее свойство целевого
 объекта является неконфигурируемым и обёрнуто в геттер и сеттер, где
 сеттер равен `undefined`.
 |
| `handler.set()` |
 Property assignment: `proxy[foo] = bar` and
 `proxy.foo = bar`Inherited property assignment:
 `Object.create(proxy)[foo] = bar``Reflect.set()` (en-US) | * Нельзя изменить значение свойства на значение, отличное от значения
 соответствующего свойства целевого объекта, если это свойство
 целевого объекта доступно только для чтения, и является
 неконфигурируемым.
* Нельзя установить значение свойства, если соответствующее свойство
 целевого объекта является неконфигурируемым, и обёрнуто в геттер и
 сеттер, где сеттер равен `undefined`.
* В строгом режиме, возвращение `false` из обработчика
 `set` вызовет ошибку `TypeError`.
 |
| `handler.deleteProperty()` |
 Property deletion: `delete proxy[foo]` and
 `delete proxy.foo``Reflect.deleteProperty()` |
 Свойство не может быть удалено, если оно существует в целевом объекте
 как собственное, неконфигурируемое свойство.
  |
| `handler.enumerate()` |
 Property enumeration / for...in:
 `for (var name in proxy) {...}``Reflect.enumerate()` | `Метод enumerate`
 должен возвращать объект.
  |
| `handler.ownKeys()` (en-US) | `Object.getOwnPropertyNames()``Object.getOwnPropertySymbols()``Object.keys()``Reflect.ownKeys()` | * Метод `ownKeys` должен возвращать список.
* Типом каждого элемента в возвращаемом списке должен быть
 `String` или `Symbol`.
* Возвращаемый список должен содержать ключи для всех
 неконфигурируемых, собственных свойств целевого объекта.
* Если целевой объект является нерасширяемым, возвращаемый список
 должен содержать все ключи для собственных полей целевого объекта и
 больше никаких других значений.
 |
| `handler.apply()` (en-US) | `proxy(..args)``Function.prototype.apply()` and
 `Function.prototype.call()``Reflect.apply()` | Ограничений нет. |
| `handler.construct()` (en-US) | `new proxy(...args)``Reflect.construct()` | Обработчик должен возвращать `Object`. |

Отзываемый `Proxy`
------------------

Метод `Proxy.revocable()` создаёт отзываемый объект `Proxy`. Такой прокси объект может быть отозван функцией `revoke`, которая отключает все ловушки-обработчики. После этого любые операции над прокси объектом вызовут ошибку `TypeError`.

```js
var revocable = Proxy.revocable(
  {},
  {
    get: function (target, name) {
      return "[[" + name + "]]";
    },
  },
);
var proxy = revocable.proxy;
console.log(proxy.foo); // "[[foo]]"

revocable.revoke();

console.log(proxy.foo); // ошибка TypeError
proxy.foo = 1; // снова ошибка TypeError
delete proxy.foo; // опять TypeError
typeof proxy; // "object", для метода typeof нет ловушек
```

Рефлексия
---------

`Reflect` это встроенный объект, предоставляющий методы для перехватываемых операций JavaScript. Это те же самые методы, что имеются в обработчиках Proxy (en-US). Объект `Reflect` не является функцией.

`Reflect` помогает при пересылке стандартных операций из обработчика к целевому объекту.

Например, метод `Reflect.has()` (en-US) это тот же `оператор in` но в виде функции:

```js
Reflect.has(Object, "assign"); // true
```

### Улучшенная функция `apply`

В ES5 обычно используется метод `Function.prototype.apply()` для вызова функции в определённом контексте (с определённым `this)` и с параметрами, заданными в виде массива (или массива-подобного объекта).

```js
Function.prototype.apply.call(Math.floor, undefined, [1.75]);
```

С методом `Reflect.apply` эта операция менее громоздка и более понятна:

```js
Reflect.apply(Math.floor, undefined, [1.75]);
// 1;

Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);
// "hello"

Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index;
// 4

Reflect.apply("".charAt, "ponies", [3]);
// "i"
```

### Проверка успешности определения нового свойства

Метод `Object.defineProperty`, в случае успеха операции, возвращает объект, а при неудаче вызывает ошибку `TypeError`. Из-за этого определение свойств требует обработки блоком `try...catch` для перехвата возможных ошибок. Метод `Reflect.defineProperty`, в свою очередь, возвращает успешность операции в виде булева значения, благодаря чему возможно использование простого `if...else` условия:

```js
if (Reflect.defineProperty(target, property, attributes)) {
  // успех
} else {
  // что-то пошло не так
}
```

* « Предыдущая статья


Итераторы и  генераторы - JavaScript

Итераторы и генераторы
======================

* « Предыдущая статья (en-US)
* Следующая статья »

Обработка каждого элемента коллекции является весьма распространённой операцией. JavaScript предоставляет несколько способов перебора коллекции, от простого цикла `for` до `map()`, `filter()` и array comprehensions (en-US). Итераторы и генераторы внедряют концепцию перебора непосредственно в ядро языка и обеспечивают механизм настройки поведения `for...of` циклов.

Подробнее см. также:

* Iteration protocols
* `for...of`
* `function*` и `Generator`
* `yield` и `yield*`
* Generator comprehensions (en-US)
Экспериментальная возможность
Итераторы
---------

Объект является итератором, если он умеет обращаться к элементам коллекции по одному за раз, при этом отслеживая своё текущее положение внутри этой последовательности. В JavaScript итератор - это объект, который предоставляет метод next(), возвращающий следующий элемент последовательности. Этот метод возвращает объект с двумя свойствами: done и value.

После создания, объект-итератор может быть явно использован, с помощью вызовов метода next().

```js
function makeIterator(array) {
  var nextIndex = 0;

  return {
    next: function () {
      return nextIndex < array.length
        ? { value: array[nextIndex++], done: false }
        : { done: true };
    },
  };
}
```

После инициализации, метод next() может быть вызван для поочерёдного доступа к парам ключ-значение в объекте:

```js
var it = makeIterator(["yo", "ya"]);
console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done); // true
```

Генераторы
----------

В то время как пользовательские итераторы могут быть весьма полезны, при их программировании требуется уделять серьёзное внимание поддержке внутреннего состояния. **Генераторы** предоставляют мощную альтернативу: они позволяют определить алгоритм перебора, написав единственную функцию, которая умеет поддерживать собственное состояние.

Генераторы - это специальный тип функции, который работает как фабрика итераторов. Функция становится генератором, если содержит один или более `yield` операторов и использует `function*` синтаксис.

```js
function\* idMaker() {
  var index = 0;
  while (true) yield index++;
}

var it = idMaker();

console.log(it.next().value); // 0
console.log(it.next().value); // 1
console.log(it.next().value); // 2
// ...
```

Итерируемые объекты
-------------------

Объект является итерируемым, если в нем определён способ перебора значений, то есть, например, как значения перебираются в конструкции `for..of`. Некоторые встроенные типы, такие как `Array` или `Map` (en-US), по умолчанию являются итерируемыми, в то время как другие типы, как, например, `Object`, таковыми не являются.

Чтобы быть итерируемым, объект обязан реализовать метод **@@iterator**, что означает, что он (или один из объектов выше по цепочке прототипов) обязан иметь свойство с именем `Symbol.iterator`:

### Пользовательские итерируемые объекты

Мы можем создать свои собственные итерируемые объекты так:

```js
var myIterable = {};
myIterable[Symbol.iterator] = function\* () {
  yield 1;
  yield 2;
  yield 3;
};
[...myIterable]; // [1, 2, 3]
```

### Встроенные итерируемые объекты

Объекты `String`, `Array`, `TypedArray`, `Map` (en-US) и `Set` являются итерируемыми, потому что их прототипы содержат метод `Symbol.iterator`.

### Синтаксис для итерируемых объектов

Некоторые выражения работают с итерируемыми объектами, например, `for-of` циклы, spread operator, `yield*`, и destructuring assignment.

```js
for (let value of ["a", "b", "c"]) {
  console.log(value);
}
// "a"
// "b"
// "c"

[..."abc"]; // ["a", "b", "c"]

function\* gen() {
  yield\* ["a", "b", "c"];
}

gen().next()[(a, b, c)] = // { value:"a", done:false }
  new Set(["a", "b", "c"]);
a; // "a"
```

Продвинутые генераторы
----------------------

Генераторы вычисляют результаты своих yield выражений по требованию, что позволяет им эффективно работать с последовательностями с высокой вычислительной сложностью, или даже с бесконечными последовательностями, как продемонстрировано выше.

`Метод` `next()` также принимает значение, которое может использоваться для изменения внутреннего состояния генератора. Значение, переданное в next(), будет рассматриваться как результат последнего yield выражения, которое приостановило генератор.

Вот генератор чисел Фибоначчи, использующий `next(x)` для перезапуска последовательности:

```js
function\* fibonacci() {
  var fn1 = 1;
  var fn2 = 1;
  while (true) {
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    var reset = yield current;
    if (reset) {
      fn1 = 1;
      fn2 = 1;
    }
  }
}

var sequence = fibonacci();
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next().value); // 3
console.log(sequence.next().value); // 5
console.log(sequence.next().value); // 8
console.log(sequence.next().value); // 13
console.log(sequence.next(true).value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next().value); // 3
```

**Примечание:** Интересно, что вызов `next(undefined)` равносилен вызову `next()`. При этом вызов next() для нового генератора с любым аргументом, кроме undefined, спровоцирует исключение `TypeError`.

Можно заставить генератор выбросить исключение, вызвав его метод `throw()` и передав в качестве параметра значение исключения, которое должно быть выброшено. Это исключение будет выброшено из текущего приостановленного контекста генератора так, будто текущий приостановленный `yield` оператор являлся `throw` оператором.

Если `yield` оператор не встречается во время обработки выброшенного исключения, то исключение передаётся выше через вызов `throw()`, и результатом последующих вызовов `next()` будет свойство `done` равное `true`.

У генераторов есть метод `return(value)`, который возвращает заданное значение и останавливает работу генератора.

* « Предыдущая статья (en-US)
* Следующая статья »


Типизированные массивы JavaScript - JavaScript

Типизированные массивы JavaScript
=================================

Типизированные массивы в JavaScript являются массивоподобными объектами, предоставляющими механизм доступа к сырым двоичным данным. Как вы уже можете знать, массив `Array` растёт и обрезается динамически, и может содержать элементы любого типа JavaScript. Благодаря оптимизациям JavaScript движков, массивы остаются быстрыми. Однако, со временем, веб-приложения становятся все более и более мощными, появляется необходимость работы с аудио- и видео-данными, требуется доступ к сырым данным WebSocket, и так далее. Становится очевидным, что возможность быстрой и эффективной работы с двоичными данными в JavaScript будет очень полезной, для чего типизированные массивы и предназначены.

Не следует путать типизированные массивы с обычными массивами: так, например, вызов `Array.isArray()` для типизированного массива вернёт `false`. Более того, не все методы, доступные для обычных массивов поддерживаются типизированными массивами (например, push и pop).

Буферы и представления: архитектура типизированных массивов
-----------------------------------------------------------

Для достижения максимальной гибкости и производительности, реализация типизированных массивов в JavaScript разделена на **буферы** и **представления**. Буфер (`ArrayBuffer`) –– это объект, представляющий из себя набор данных. Он не имеет формата и не предоставляет возможности доступа к своему содержимому. Для доступа к памяти буфера вам нужно использовать представление. Представление предоставляет контекст: тип данных, начальную позицию в буфере и количество элементов. Это позволяет представить данные в виде типизированного массива.

![Typed arrays in an ArrayBuffer](/ru/docs/Web/JavaScript/Guide/Typed_arrays/typed_arrays.png)

### ArrayBuffer

Объект `ArrayBuffer` –– это набор бинарных данных с фиксированной длиной. Вы не можете манипулировать содержимым `ArrayBuffer` напрямую. Вместо этого, необходимо создать типизированное представление `DataView`, которое будет отображать буфер в определённом формате, и даст доступ на запись и чтение его содержимого.

### Типизированные представления

Название типизированного представления массива говорит само за себя. Оно представляет массив в распространённых числовых форматах, таких как `Int8`, `Uint32`, `Float64` и так далее. Среди прочих, существует специальное представление `Uint8ClampedArray`. Оно ограничивает значения интервалом от 0 до 255. Это полезно, например, при Обработке данных изображения в Canvas.

### DataView

Объект `DataView` –– это низкоуровневый интерфейс, предоставляющий API для записи/чтения произвольных данных в буфер. Это полезно при работе с различными типами данных, например. В то время как типизированные представления всегда имеют порядок байт (смотрите Endianness (en-US)) соответствующий используемому в вашей операционной системе, `DataView` позволяет контроллировать порядок байт (byte-order). По умолчанию это big-endian, но через API можно установить little-endian.

Веб API, использующие типизированные массивы
--------------------------------------------

`FileReader.prototype.readAsArrayBuffer()`

Метод `FileReader.prototype.readAsArrayBuffer()` читает содержимое заданного `Blob` или `File`.

`XMLHttpRequest.prototype.send()`

Метод `send()` экземпляра `XMLHttpRequest` теперь поддерживает в качестве аргумента `ArrayBuffer`.

`ImageData.data`

Имеет тип `Uint8ClampedArray` и представляет изображение в виде одномерного массива, где цветовые компоненты расположены в порядке RGBA, и их значения принудительно ограничены диапазоном от 0 до 255.

Примеры
-------

### Использование представлений с буферами

Прежде всего, необходимо создать буфер с фиксированной длиной 16 байт:

```js
var buffer = new ArrayBuffer(16);
```

На данном этапе мы имеем область памяти в 16 байт, инициализированной нулевыми значениями. Всё, что мы можем сделать сейчас, это убедиться, что длина буфера действительно 16 байт:

```js
if (buffer.byteLength === 16) {
  console.log("Да, это 16 байт.");
} else {
  console.log("О нет, размер не наш!");
}
```

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

```js
var int32View = new Int32Array(buffer);
```

Теперь мы можем получить доступ к элементам представления как к элементам обычного массива:

```js
for (var i = 0; i < int32View.length; i++) {
  int32View[i] = i \* 2;
}
```

Этот код поместит 4 элемента в буфер (4 элемента по 4 байта даст 16 байт) со следующими значениям: `0`, `2`, `4` и `6`.

### Множество представлений для одних и тех же данных

Всё становится намного интереснее, если создать несколько разных представлений для одного и того же буфера. Например, приведённый выше код можно дополнить следующим образом:

```js
var int16View = new Int16Array(buffer);

for (var i = 0; i < int16View.length; i++) {
  console.log("Entry " + i + ": " + int16View[i]);
}
```

Здесь мы создаём 16-битное целочисленное представление, которое ссылается на тот же самый буфер, что и 32-битное представление, и затем выводим все 16-битные элементы этого представления. Мы получим следующий вывод: 0, 0, 2, 0, 4, 0, 6, 0.

Можно пойти дальше. Оцените этот код:

```js
int16View[0] = 32;
console.log("Элемент 0 в 32-битном представлении теперь равен " + int32View[0]);
```

Результатом выполнения станет текст: "Элемент 0 в 32-битном представлении теперь равен 32". Другими словами, два массива на самом деле являются лишь разными представлениями одного и того же буфера данных в разных форматах. Вы можете повторить это с представлениями любого типа.

### Работа со сложными структурами данных

Комбинируя буфер и множество представлений разного формата, имеющих разные смещения относительно начала буфера, можно управляться с объектами содержащими разнородные данные. Это позволяет, к примеру, взаимодействовать со сложными структурам из WebGL, файлами данных или структурами языка C (сопоставление данных JS и C).

Рассмотрим следующую структуру из языка C:

```cpp
struct someStruct {
  unsigned long id;
  char username[16];
  float amountDue;
};
```

Получить доступ к полям этой структуры можно следующим образом:

```js
var buffer = new ArrayBuffer(24);

// ... поместить данные структуры в буфер ...

var idView = new Uint32Array(buffer, 0, 1);
var usernameView = new Uint8Array(buffer, 4, 16);
var amountDueView = new Float32Array(buffer, 20, 1);
```

Теперь получить или изменить значение поля `amountDue`, к примеру, можно путём обращения к `amountDueView[0]`.

**Примечание:** Выравнивание данных в языке C является платформозависимым. Принимайте меры по вычислению правильных отступов в данных с учётом выравнивания.

### Преобразование в обычные массивы

Иногда после обработки типизированного массива бывает полезно конвертировать его в обычный массив, чтобы получить доступ к методам прототипа `Array`. Для этих целей существует метод `Array.from`. А в тех случаях, когда `Array.from` не поддерживается, используйте следующий код:

```js
var typedArray = new Uint8Array([1, 2, 3, 4]),
  normalArray = Array.prototype.slice.call(typedArray);
normalArray.length === 4;
normalArray.constructor === Array;
```

Смотрите также
--------------

* Получение `ArrayBuffer`s и типизированных массивов из *Base64 кодировки* (en-US)
* `StringView` – библиотека для работы со строками в стиле языка С, основанная на типизированных массивах
* Быстрая работа с пикселями Canvas через типизированные массивы
* Типизированные массивы: Двоичные данные в браузере
* Endianness (en-US)


Циклы и итерации - JavaScript

Циклы и итерации
================

* « Предыдущая статья
* Следующая статья »

Циклы - простой способ сделать какое-то действие несколько раз. Эта глава руководства JavaScript Guide познакомит вас с различными операторами доступными в JavaScript.

Вы можете представить цикл в виде компьютеризированной версии игры, где вы говорите кому-то сделать X шагов в одном направлении, затем Y шагов в другом; для примера, идея игры "Иди 5 шагов на восток" может быть выражена в виде цикла:

```js
var step;
for (step = 0; step < 5; step++) {
  // Запускается 5 раз, с шагом от 0 до 4.
  console.log("Идём 1 шаг на восток");
}
```

Существует множество различных видов циклов, но все они по сути делают тоже самое: повторяют какое-либо действие несколько раз (не забывайте про нулевой раз повторения, отсчёт в массиве начинается с 0). Различные по строению циклы предлагают разные способы для определения начала и окончания цикла. Для различных задач программирования существуют свои операторы цикла, с помощью которых они решаются намного проще.

Операторы предназначенные для организации циклов в JavaScript:

* Цикл\_for
* Цикл\_do...while
* Цикл\_while
* Метка (label)
* break
* continue
* for...in
* for...of
Цикл `for`
----------

Цикл **`for`** повторяет действия, пока не произойдёт какое-либо специальное событие завершения цикла. Оператор `for` в JavaScript аналогичен оператору for в Java и C. Объявление оператора `for` выглядит следующим образом:
```

for ([начало]; [условие]; [шаг]) выражения
```

При его выполнении происходит следующее:

1. Выполняется выражение `начало`, если оно указано. Это выражение обычно инициализирует один или несколько счётчиков, но синтаксис позволяет выражению быть любой сложности. Также используется для объявления переменных.
2. Выполняется `условие`. Если `условие` истинно, то выполняются `выражения`. Если оно ложно, цикл `for` прерывается. Если же `условие` полностью пропущено, то оно считается истинным.
3. Выполняются `выражения`. Чтобы выполнить несколько выражений, используются блок-выражение `{ ... }` для группировки выражений.
4. Обновляется `шаг`, если он есть, а затем управление возвращается к шагу 2.
### Пример

В следующей функции есть цикл `for`, который считает количество выбранных жанров в списке прокрутки (элемент `<select>`, который позволяет выбрать несколько элементов). Цикл `for` объявляет переменную `i` и задаёт ей значение 0. Также он проверяет, что `i` меньше количества элементов в элементе `<select>`, выполняет оператор `if` и увеличивает `i` на один после каждого прохода цикла.

```html
<form name="selectForm">
  <p>
    <label for="musicTypes"
 >Выберите некоторые жанры музыки, а затем нажмите на кнопку ниже:</label
 >
    <select id="musicTypes" name="musicTypes" multiple="multiple">
      <option selected="selected">R&B</option>
      <option>Jazz</option>
      <option>Blues</option>
      <option>New Age</option>
      <option>Classical</option>
      <option>Opera</option>
    </select>
  </p>
  <p><input id="btn" type="button" value="Как много выбрано?" /></p>
</form>

<script>
 function howMany(selectObject) {
 var numberSelected = 0;
 for (var i = 0; i < selectObject.options.length; i++) {
 if (selectObject.options[i].selected) {
 numberSelected++;
 }
 }
 return numberSelected;
 }

 var btn = document.getElementById("btn");
 btn.addEventListener("click", function () {
 alert("Выбрано элементов: " + howMany(document.selectForm.musicTypes));
 });
</script>
```

Цикл `do...while`
-----------------

Цикл `do...while` повторяется пока заданное условие истинно. Оператор `do...while` имеет вид:
```

do
  выражения
while (условие);
```

`выражения` выполняются пока `условие` истинно. Чтобы использовать несколько выражений, используйте блок-выражение `{ ... }`, чтобы сгруппировать их. Если `условие` истинно, `выражения` выполнятся снова. В конце каждого прохода `условие` проверяется. Если `условие` ложно, выполнение приостанавливается и управление передаётся выражению после `do...while`.

### Пример

В следующем примере, цикл `do` выполнится минимум 1 раз и запускается снова, пока `i` меньше 5.

```js
do {
  i += 1;
  console.log(i);
} while (i < 5);
```

Цикл `while`
------------

Цикл `while` выполняет выражения пока условие истинно. Выглядит он так:
```

while (условие)
  выражения
```

Если `условие` становится ложным, выражения в цикле перестают выполняться и управление переходит к выражению после цикла.

`Условие` проверяется на истинность до того, как выполняются `выражения` в цикле. Если `условие` истинно, выполняются `выражения`, а затем условие проверяется снова. Если `условие` ложно, выполнение приостанавливается и управление переходит к выражению после `while`.

Чтобы использовать несколько выражений, используйте блок выражение `{ ... }`, чтобы сгруппировать их.

### Пример 1

Следующий цикл `while` работает, пока `n` меньше трёх:

```js
var n = 0;
var x = 0;
while (n < 3) {
  n++;
  x += n;
}
```

С каждой итерацией, цикл увеличивает `n` и добавляет это значение к `x`. Поэтому, `x` и `n` получают следующие значения:

* После первого прохода: `n` = 1 и `x` = 1
* После второго: `n` = 2 и `x` = 3
* После третьего прохода: `n` = 3 и `x` = 6

После третьего прохода, условие `n < 3` становится ложным, поэтому цикл прерывается.

### Пример 2

Избегайте бесконечных циклов. Убедитесь, что условие цикла в итоге станет ложным; иначе, цикл никогда не прервётся. Выражения в следующем цикле `while` будут выполняться вечно, т.к. условие никогда не станет ложным:

```js
while (true) {
  console.log("Hello, world");
}
```

Метка (label)
-------------

Метка представляет собой оператор с идентификатором, который позволяет вам ссылаться на какое-то место в вашей программе. Например, вы можете использовать метку, чтобы обозначить цикл, а затем использовать операторы `break` или `continue`, чтобы указать, должна ли программа прерывать цикл или продолжать его выполнение.

Синтаксис метки следующий:
```

метка :
   оператор
```

Значение *метки* может быть любым корректным JavaScript идентификатором, не являющимся зарезервированным словом. `Оператор`*,* указанный вами после метки может быть любым выражением.

### Пример

В этом примере, метка `markLoop` обозначает цикл `while`.

```js
markLoop: while (theMark == true) {
  doSomething();
}
```

`break`
-------

Используйте оператор `break`, чтобы прерывать цикл, переключать управление или в сочетании с оператором метка.

* Когда вы используете `break` без метки, он прерывает циклы `while`, `do-while` и `for` или сразу переключает управление к следующему выражению.
* Когда вы используете `break` с меткой, он прерывает специально отмеченное выражение.

Синтаксис оператора может быть таким:

1. `break;`
2. `break Метка;`

Первая форма синтаксиса прерывает цикл совсем или переключает управление; вторая прерывает специально обозначенное выражение.

### **Пример** **1**

Следующий пример проходит по элементам в массиве, пока не найдёт элемент, чьё значение - `theValue`:

```js
for (i = 0; i < a.length; i++) {
  if (a[i] == theValue) {
    break;
  }
}
```

### Пример 2: Прерывание метки

```js
var x = 0;
var z = 0;
labelCancelLoops: while (true) {
  console.log("Внешний цикл: " + x);
  x += 1;
  z = 1;
  while (true) {
    console.log("Внутренний цикл: " + z);
    z += 1;
    if (z === 10 && x === 10) {
      break labelCancelLoops;
    } else if (z === 10) {
      break;
    }
  }
}
```

`continue`
----------

Оператор `continue` используется, чтобы шагнуть на шаг вперёд в циклах `while`, `do-while`, `for` или перейти к метке.

* Когда вы используете `continue` без метки, он прерывает текущую итерацию циклов `while`, `do-while` и `for` и продолжает выполнение цикла со следующей итерации. В отличие от `break`, `continue` не прерывает выполнение цикла полностью. В цикле `while` он прыгает к условию. А в `for` увеличивает `шаг`.
* Когда вы используете `continue` с меткой, он применяется к циклу с этой меткой.

Синтаксис `continue` может выглядеть так:

1. `continue;`
2. `continue` Метка `;`
### Пример 1

Следующий пример показывает цикл `while` с оператором `continue`, который срабатывает, когда значение `i` равно 3. Таким образом, `n` получает значения 1, 3, 7 и 12.

```js
var i = 0;
var n = 0;
while (i < 5) {
  i++;
  if (i == 3) {
    continue;
  }
  n += i;
}
```

### Пример 2

Выражение, отмеченное `checkiandj` содержит выражение отмеченное `checkj`. При встрече с `continue`, программа прерывает текущую итерацию `checkj` и начинает следующую итерацию. Каждый раз при встрече с `continue`, `checkj` переходит на следующую итерацию, пока условие возвращает `false`. Когда возвращается `false`, после вычисления остатка от деления `checkiandj`, `checkiandj` переходит на следующую итерацию, пока его условие возвращает `false`. Когда возвращается `false`, программа продолжает выполнение с выражения после `checkiandj`.

Если у `continue` проставлена метка `checkiandj`, программа может продолжиться с начала метки `checkiandj`.

```js
checkiandj: while (i < 4) {
  console.log(i);
  i += 1;
  checkj: while (j > 4) {
    console.log(j);
    j -= 1;
    if (j % 2 != 0) {
      continue checkj;
    }
    console.log(j + " чётное.");
  }
  console.log("i = " + i);
  console.log("j = " + j);
}
```

`for...in`
----------

Оператор `for...in` проходит по всем перечислимым свойствам объекта. JavaScript выполнит указанные выражения для каждого отдельного свойства. Цикл `for...in` выглядит так:
```

for (variable in object) {
  выражения
}
```

### Пример

Следующая функция берёт своим аргументом объект и его имя. Затем проходит по всем свойствам объекта и возвращает строку, которая содержит имена свойств и их значения.

```js
function dump\_props(obj, obj\_name) {
  var result = "";
  for (var i in obj) {
    result += obj_name + "." + i + " = " + obj[i] + "<br>";
  }
  result += "<hr>";
  return result;
}
```

Для объекта `car` со свойствами `make` и `model`, *результатом* будет:

```js
car.make = Ford;
car.model = Mustang;
```

### Пример №2

Также, по ключу можно выводить значение:
```

let obj = {model: 'AUDI A8', year: '2019', color: 'brown'}

for (key in obj) {
  console.log(`${key} = ${obj[key]}`);
}
// model = AUDI A8
// year = 2019
// color = brown
```

### Массивы

Хотя, очень заманчиво использовать **for...in** как способ пройтись по всем элементам `Array`, этот оператор возвращает имя свойств определённых пользователем помимо числовых индексов. Таким образом лучше использовать стандартный `for` для числовых индексов при взаимодействии с массивами, поскольку оператор **for...in** проходит по определённым пользователем свойствам в дополнение к элементам массива, если вы изменяете массив, например, добавляете свойства и методы.

```js
let arr = ["AUDI A8", "2019", "brown"];
arr.cost = "$100.000";

for (key in arr) {
  console.log(`${key} = ${arr[key]}`);
}

// 0 = AUDI A8
// 1 = 2019
// 2 = brown
// cost = $100.000
```

`for...of`
----------

Оператор `for...of` создаёт цикл, проходящий по перечислимым объектам (en-US) (включая `Array`, `Map` (en-US), `Set`, объект arguments (en-US) и так далее), вызывая на каждой итерации функцию с выражениями, которые надо выполнить для получения значения каждого отдельного свойства.
```

for (variable of object) {
  выражения
}
```

Следующий пример показывает разницу между циклами `for...of` и `for...in`. Тогда как `for...in` проходит по именам свойств, `for...of` проходит по значениям свойств:

```js
let arr = [3, 5, 7];
arr.foo = "hello";

for (let i in arr) {
  console.log(i); // выводит "0", "1", "2", "foo"
}

for (let i of arr) {
  console.log(i); // выводит "3", "5", "7"
}
```

* « Предыдущая статья
* Следующая статья »