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

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

JavaScript предоставляет три оператора сравнения величин:

* равенство ("двойное равно") использует == (en-US),
* строгое равенство ("тройное равно" или "идентично") использует === (en-US),
* и `Object.is` (новшество из ECMAScript 6).

Выбор оператора зависит от типа сравнения, которое необходимо произвести.

В общих чертах, двойное равно перед сравнением величин производит приведение типов; тройное равно сравнивает величины без приведения (если величины разных типов, вернёт `false`, даже не сравнивая); ну и `Object.is` ведёт себя так же, как и тройное равно, но со специальной обработкой для `NaN`, `-0` и `+0`, возвращая `false` при сравнении `-0` и `+0`, и `true` для операции `Object.is(NaN, NaN)`. (В то время как двойное или тройное равенство вернут `false` согласно стандарту IEEE 754.) Следует отметить, что все эти различия в сравнениях применимы лишь для примитивов. Для любых не примитивных объектов `x` и `y`, которые имеют одинаковые структуры, но представляют собой два отдельных объекта (переменные `x` и `y` не ссылаются на один и тот же объект), все операторы сравнения вернут `false`.

Сравнение с использованием `==`
-------------------------------

Перед сравнением оператор равенства *приводит* обе величины к общему типу. После приведений (одного или обоих операндов), конечное сравнение выполняется также как и для `===`. Операция сравнения *симметрична*: `A == B` возвращает то же значение, что и `B == A` для любых значений `A` и `B`.

В следующей таблице приведены результаты сравнения оператора равенства для различных значений:

|  | `Операнд B` |
| --- | --- |
|  |  | `Undefined` | `Null` | `Number` | `String` | `Boolean` | `Object` |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `Операнд A` | `Undefined` | `true` | `true` | `false` | `false` | `false` | `false` |
| `Null` | `true` | `true` | `false` | `false` | `false` | `false` |
| `Number` | `false` | `false` | `A === B` | `A === ToNumber(B)` | `A === ToNumber(B)` | `A == ToPrimitive(B)` |
| `String` | `false` | `false` | `ToNumber(A) === B` | `A === B` | `ToNumber(A) === ToNumber(B)` | `A == ToPrimitive(B)` |
| `Boolean` | `false` | `false` | `ToNumber(A) === B` | `ToNumber(A) === ToNumber(B)` | `A === B` | `ToNumber(A) == ToPrimitive(B)` |
| `Object` | `false` | `false` | `ToPrimitive(A) == B` | `ToPrimitive(A) == B` | `ToPrimitive(A) == ToNumber(B)` | `A === B` |

В таблице выше, ToNumber(A) пытается перед сравнением привести свой аргумент к числу. Такое поведение эквивалентно `+A` (унарный оператор `+`). Если ToPrimitive(A) получает *объект* в качестве аргумента, то производятся попытки привести его к примитиву, вызывая на нем методы `A.toString` и `A.valueOf`.

Традиционно (и в соответствии с ECMAScript), ни один из объектов не равен `undefined` или `null`. Но большинство браузеров позволяет определённому классу объектов (в частности, объектам `document.all` для любой страницы) *эмулировать* значение `undefined.` Оператор равенства вернёт значение true для `null == A` и `undefined == A`, тогда, и только тогда, когда объект эмулирует значение `undefined`. Во всех остальных случаях объект не может быть равен `undefined` или `null`.

```js
var num = 0;
var obj = new String("0");
var str = "0";
var b = false;

console.log(num == num); // true
console.log(obj == obj); // true
console.log(str == str); // true

console.log(num == obj); // true
console.log(num == str); // true
console.log(obj == str); // true
console.log(null == undefined); // true

// оба false, кроме очень редких случаев
console.log(obj == null);
console.log(obj == undefined);
```

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

Строгое равенство с использованием `===`
----------------------------------------

Строгое равно проверяет на равенство две величины, при этом тип каждой из величин перед сравнением не изменяется (не приводится). Если значения имеют различающиеся типы, то они не могут быть равными. С другой стороны все не числовые переменные, принадлежащие одному типу, считаются равными между собой, если содержат одинаковые величины. Ну и, наконец, числовые переменные считаются равными, если они имеют одинаковые значения, либо одна из них `+0`, а вторая `-0`. В то же время, если хотя бы одна из числовых переменных содержит значение `NaN`, выражение вернёт `false`.

```js
var num = 0;
var obj = new String("0");
var str = "0";
var b = false;

console.log(num === num); // true
console.log(obj === obj); // true
console.log(str === str); // true

console.log(num === obj); // false
console.log(num === str); // false
console.log(obj === str); // false
console.log(null === undefined); // false
console.log(obj === null); // false
console.log(obj === undefined); // false
```

Практически всегда для сравнения следует использовать оператор строгого равенства. Для всех значений, за исключением числовых используется очевидная семантика: величина равна только сама себе. Как было сказано выше для числовых типов можно выделить два особых случая. Во-первых, сравнение `+0` и `-0`. Знак для нуля введён для упрощения некоторых вычислений с плавающей запятой, однако, с точки зрения математики, разницы между `+0` и `-0` не существует, поэтому оператор строгого равенства считает их равными. Во-вторых, сравнение величин `NaN`. `NaN` (Not a number) представляет из себя значение не определённой величины, которое применяется для не чётко определённых математических задач (например `+∞ + -∞`). Для оператора строго равенства `NaN` не равен ни одной из величин, в том числе и самому себе (единственный случай, когда `(x!==x)` вернёт true).

Равенство одинаковых величин
----------------------------

Равенство одинаковых величин определяет *функциональную идентичность* во всех контекстах сравниваемых величин. (Данный способ сравнения основывается на принципе подстановки Барбары Лисков.) Рассмотрим пример попытки изменения неизменяемого (immutable) свойства`:`

```js
// Добавление неизменяемого свойства NEGATIVE\_ZERO (отрицательный ноль) в конструктор Number.
Object.defineProperty(Number, "NEGATIVE\_ZERO", {
  value: -0,
  writable: false,
  configurable: false,
  enumerable: false,
});

function attemptMutation(v) {
  Object.defineProperty(Number, "NEGATIVE\_ZERO", { value: v });
}
```

При попытке изменения неизменяемого свойства, вызов `Object.defineProperty` выбросит исключение, однако, если новое свойство равняется старому, изменений не произойдёт и исключение не будет выброшено. Если `v` содержит `-0`, изменений не произойдёт, а значит, код отработает без выброса исключений. Однако, если же `v` содержит `+0`, `Number.NEGATIVE_ZERO` утратит свою неизменяемую величину.

Именно для сравнения нового и текущего неизменяемых свойств используется сравнение одинаковых величин, представленное методом `Object.is`.

Равенство одинаковых величин и нулей
------------------------------------

Аналогично равенству одинаковых величин, но +0 и -0 считаются равными.

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

```js
function sameValueZero(x, y) {
  if (typeof x === "number" && typeof y === "number") {
    // x и y равны (могут быть -0 и 0) или они оба равны NaN
    return x === y || (x !== x && y !== y);
  }
  return x === y;
}
```

*Равенство одинаковых величин и нулей* отличается от *строгого равенства* тем, что принимает каждое значение `NaN` равным любому другому значению `NaN`, а от *равенства одинаковых величин* тем, что принимает `-0` равным `0`. Подобное поведение обычно оказывается самым уместным при поиске в списках, особенно при работе с `NaN`. Данная стратегия сравнения используется в методах `Array.prototype.includes()`, `TypedArray.prototype.includes()` (en-US), а так же в `Map` (en-US) и `Set` для оценки равенства ключей.

Спецификации для равенства, строгого равенства и равенства одинаковых величин
-----------------------------------------------------------------------------

В стандарте ES5, сравнение выполняемое оператором == (en-US) описывается в секции 11.9.3, The Abstract Equality Algorithm. Описание оператора === (en-US) находится в секции 11.9.6, The Strict Equality Algorithm. В секции 9.12, The SameValue Algorithm ES5 описывает операцию сравнение одинаковых величин для внутреннего движка JS. Строгое равенство и равенство одинаковых величин, практически одинаковы, за исключением обработки числовых типов. ES6 предлагает использовать алгоритм сравнения одинаковых величин через вызов `Object.is`.

### Как понимать все эти способы сравнения?

До выхода редакции ES6 считалось, что оператор строгого равенства просто "улучшенная" версия оператора нестрогого равенства. Например, некоторые считали, что == просто улучшенная версия === потому, что первый оператор делает всё то же, что и второй, плюс приведение типов своих операндов. То есть 6 == "6". (Или же наоборот: оператор нестрогого равенства базовый, а оператор строгого равенства просто его улучшенная версия, ведь он добавляет ещё одно условие - требует, чтобы оба операнда были одного и того же типа. Какой вариант ближе вам, зависит только от вашей точки зрения на вещи.)

Но эти точки зрения уже нельзя применить к новому методу сравнения Object.is из новой редакции ES6. Нельзя сказать, что Object.is более или менее строг относительно существующих способов сравнения, или что это нечто среднее между ними. Ниже в таблице показаны основные различия операторов сравнения. Object.is интересен тем, что различает -0 и +0, а также умеет сравнивать два не числа NaN.

| x | y | `==` | `===` | `Object.is` | `SameValueZero` |
| --- | --- | --- | --- | --- | --- |
| `undefined` | `undefined` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `null` | `null` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `true` | `true` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `false` | `false` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `'foo'` | `'foo'` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `0` | `0` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `+0` | `-0` | `✅ true` | `✅ true` | `❌ false` | `✅ true` |
| `+0` | `0` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `-0` | `0` | `✅ true` | `✅ true` | `❌ false` | `✅ true` |
| `0n` | `-0n` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `0` | `false` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `""` | `false` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `""` | `0` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `'0'` | `0` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `'17'` | `17` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `[1, 2]` | `'1,2'` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `new String('foo')` | `'foo'` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `null` | `undefined` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `null` | `false` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `undefined` | `false` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `{ foo: 'bar' }` | `{ foo: 'bar' }` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `new String('foo')` | `new String('foo')` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `0` | `null` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `0` | `NaN` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `'foo'` | `NaN` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `NaN` | `NaN` | `❌ false` | `❌ false` | `✅ true` | `✅ true` |

### Когда использовать Object.is(), а когда ===

Особое поведение `Object.is` по отношению к нулям, вероятно, будет представлять интерес при реализации определенных схем метапрограммирования, особенно в отношении дескрипторов свойств, когда желательно, чтобы ваш алгоритм имел такое же поведение, как `Object.defineProperty`. В случае, если вам этого не требуется, рекомендуется избегать `Object.is` и вместо этого использовать `===`. Как правило, даже при необходимости сравнения `NaN`-значений, эффективнее выполнить проверку таких значений отдельно, при помощи метода `isNaN`, чем выяснять, как окружающие вычисления могут повлиять на знаки нулей и как это отразится на вашем алгоритме.

Вот неполный список встроенных методов и операторов, при использовании которых, различие между `-0` и `+0` может оказаться важным для вашего кода:

`-` (unary negation) (en-US)

Рассмотрим следующий пример:

```js
const stoppingForce = obj.mass \* -obj.velocity;
```

Если `obj.velocity` равно `0` (или вычисляется как `0`), в этом месте возникает `-0` и присваивается в `stoppingForce`.

`Math.atan2`, `Math.ceil`, `Math.pow`, `Math.round`

In some cases, it's possible for a `-0` to be introduced into an expression as a return value of these methods even when no `-0` exists as one of the parameters. For example, using `Math.pow` to raise `-Infinity` to the power of any negative, odd exponent evaluates to `-0`. Refer to the documentation for the individual methods.

`Math.floor`, `Math.max`, `Math.min`, `Math.sin`, `Math.sqrt`, `Math.tan`

It's possible to get a `-0` return value out of these methods in some cases where a `-0` exists as one of the parameters. E.g., `Math.min(-0, +0)` evaluates to `-0`. Refer to the documentation for the individual methods.

`~`, `<<`, `>>`

Each of these operators uses the ToInt32 algorithm internally. Since there is only one representation for 0 in the internal 32-bit integer type, `-0` will not survive a round trip after an inverse operation. E.g., both `Object.is(~~(-0), -0)` and `Object.is(-0 << 2 >> 2, -0)` evaluate to `false`.

Relying on `Object.is` when the signedness of zeros is not taken into account can be hazardous. Of course, when the intent is to distinguish between `-0` and `+0`, it does exactly what's desired.

### Caveat: Object.is() and NaN

The `Object.is` specification treats all instances of `NaN` as the same object. However, since typed arrays are available, we can have distinct floating point representations of `NaN` which don't behave identically in all contexts. For example:

```js
const f2b = (x) => new Uint8Array(new Float64Array([x]).buffer);
const b2f = (x) => new Float64Array(x.buffer)[0];
// Get a byte representation of NaN
const n = f2b(NaN);
// Change the first bit, which is the sign bit and doesn't matter for NaN
n[0] = 1;
const nan2 = b2f(n);
console.log(nan2); // NaN
console.log(Object.is(nan2, NaN)); // true
console.log(f2b(NaN)); // Uint8Array(8) [0, 0, 0, 0, 0, 0, 248, 127]
console.log(f2b(nan2)); // Uint8Array(8) [1, 0, 0, 0, 0, 0, 248, 127]
```

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

* JS Comparison Table


Повторное введение в JavaScript (JS учебник) - JavaScript

Повторное введение в JavaScript (JS учебник)
============================================

Почему повторное введение? Потому что JavaScript известен тем, что является самым неправильно понятым языком программирования в мире. Его часто называют игрушкой, но под слоем обманчивой простоты ожидают мощные языковые возможности. В настоящее время JavaScript используется невероятным количеством высококлассных приложений, показывая, что углублённое знание этой технологии является важным навыком для любого веб или мобильного разработчика.

Было бы полезно начать с истории языка. JavaScript был создан в 1995 Бренданом Айком, инженером в компании Netscape. Первый релиз состоялся вместе с выходом браузера Netscape 2 в начале 1996 года. Сначала язык назывался LiveScript, но затем был переименован в связи с маркетинговыми целями, чтобы сыграть на популярности языка Java компании Sun Microsystem — несмотря на это языки практически не имеют ничего общего друг с другом. Так было положено начало путаницы между этими языками.

Чуть позже Microsoft выпустила очень похожий и практически совместимый язык JScript, который шёл вместе с IE3. Через пару месяцев Netscape отправил язык в Ecma International, Европейскую организацию занимающуюся стандартами, которая выпустила первую версию стандарта ECMAScript в 1997. Стандарт получил значимое обновление в ECMAScript edition 3 в 1999, и остаётся самым стабильным до сегодняшнего дня. Четвёртая версия была отклонена, из-за проблем с усложнениями в языке. Многие вещи из четвёртого издания послужили основой для стандарта ECMAScript 5 (декабрь 2009) и ECMAScript 6 (июнь 2015).

**Примечание:** Далее по тексту мы будем называть язык ECMAScript как "JavaScript".

В отличие от большинства языков, JavaScript не следует концепции ввода (input) и вывода (output). Он спроектирован таким образом, чтобы запускаться как язык сценариев, встроенный в среду исполнения. Самая популярная среда исполнения это браузер, однако интерпретаторы JavaScript присутствуют и в Adobe Acrobat, Photoshop, Yahoo!'s Widget engine, и даже в серверном окружении, например node.js.

Описание
--------

JavaScript является объектно-ориентированным языком, имеющий типы и операторы, встроенные объекты и методы. Его синтаксис происходит от языков Java и C, поэтому много конструкций из этих языков применимы и к JavaScript. Одним из ключевых отличий JavaScript является отсутствие классов, вместо этого функциональность классов осуществляется прототипами объектов (смотрите ES6 `Classes`) . Другое главное отличие в том, что функции это объекты, в которых содержится исполняемый код и которые могут быть переданы куда-либо, как и любой другой объект.

Начнём с основы любого языка: с типов данных. Программы на JavaScript оперируют значениями, и все эти значения принадлежат к определённому типу. Типы данных в JavaScript:

* Числа
* Строки
* Логические типы
* Функции
* Объекты
* Символы (новый тип из шестой редакции)

Да, ещё Undefined и Null, которые немного обособлены. И Массивы, которые являются особым видом объектов. А также Даты и Регулярные выражения, тоже являющиеся объектами. И, если быть технически точным, функции это тоже особый вид объекта. Поэтому схема типов выглядит скорее так:

* Числа
* Строки
* Логические типы
* Символы (новый тип из шестой редакции)
* Объекты
    + Функции
    + Массивы
    + Даты
    + Регулярные выражения
* Null
* undefined

Также есть несколько встроенных типов Ошибок. Чтобы было проще, рассмотрим подробнее первую схему.

Числа
-----

Числа в JavaScript — это "64-битные значения двойной точности формата IEEE 754", согласно спецификации. Это имеет интересные последствия. В JavaScript нет такой вещи, как целое число, поэтому с арифметикой нужно быть начеку, если вы привыкли к вычислениям в языках C или Java. Взгляните на пример:

```js
0.1 + 0.2 == 0.30000000000000004;
```

На практике целые значения это 32-битные целые (и хранятся таким образом в некоторых браузерных реализациях), что может быть важно для побитовых операций.

Поддерживаются стандартные арифметические операторы (en-US), включая сложение, вычитание, остаток от деления и т.д. Есть ещё встроенный объект, который я забыл упомянуть, называемый Math, который содержит более продвинутые математические функции и константы:

```js
Math.sin(3.5);
var circumference = Math.PI \* (r + r);
```

Вы можете преобразовать строку в целое число, используя встроенную функцию `parseInt()`. Её необязательный второй параметр — основание системы счисления, которое следует всегда явно указывать:

```js
parseInt("123", 10); // 123
parseInt("010", 10); // 10
```

Если вы не предоставите основание, то можете получить неожиданные результаты:

```js
parseInt("010"); // 8
parseInt("0x10"); // 16
```

Это случилось потому, что функция `parseInt()` расценила строку как восьмеричную из-за начального 0, а шестнадцатеричную - из-за начального "0x".

Если хотите преобразовать двоичное число в десятичное целое, просто смените основание:

```js
parseInt("11", 2); // 3
```

Вы можете аналогично парсить дробные числа, используя встроенную функцию `parseFloat()`, которая использует всегда основание 10 в отличие от родственной `parseInt()`.

Также можно использовать унарный оператор **`+`** для преобразования значения в число:

```js
+"42"; // 42
+"0x10"; // 16
```

Специальное значение NaN (сокращение от "Not a Number") возвращается, если строка не является числом:

```js
parseInt("hello", 10); // NaN
```

`NaN` "заразителен": любая математическая операция над `NaN` возвращает `NaN`:

```js
NaN + 5; // NaN
```

Проверить значение на `NaN` можно встроенной функцией `isNaN()`:

```js
isNaN(NaN); // true
```

JavaScript также имеет специальные значения `Infinity` (бесконечность) и `-Infinity`:

```js
1 / 0; // Infinity
-1 / 0; // -Infinity
```

Проверить значение на `Infinity`, `-Infinity` и `NaN` можно с помощью встроенной функции `isFinite()`:

```js
isFinite(1 / 0); // false
isFinite(-Infinity); // false
isFinite(NaN); // false
```

**Примечание:** функции `parseInt()` и `parseFloat()` обрабатывают строку до тех пор, пока не будет встречен символ, не являющийся корректным для заданного числового формата, затем эти функции возвращают число, полученное в результате обработки вплоть до этого символа. А оператор "+" просто возвращает `NaN`, если в строке есть хоть один некорректный символ. Попробуйте сами в консоли преобразовать строку "10.2abc" каждым из методов, и различие станет ясным.

Строки
------

Строки в JavaScript - это последовательности символов Unicode (в кодировке UTF-16). Для тех, кто имеет дело с интернационализацией, это должно стать хорошей новостью. Если быть более точным, то строка - это последовательность кодовых единиц, каждая из которых представлена 16-битовым числом, а каждый символ Unicode состоит из 1 или 2 кодовых единиц.

Чтобы представить единственный символ, используйте строку, содержащую только этот символ.

Чтобы выяснить длину строки (в кодовых единицах), используйте свойство `length`:

```js
"hello".length; // 5
```

Это уже первый шаг для работы с объектами! Мы уже говорили, что и строки можно использовать как объекты? У них тоже есть методы:

```js
"hello".charAt(0); // h
"hello, world".replace("hello", "goodbye"); // goodbye, world
"hello".toUpperCase(); // HELLO
```

Другие типы
-----------

JavaScript дополнительно различает такие типы, как `null`, который указывает на преднамеренное отсутствующее значение, и `undefined`, указывающий на неинициализированное значение — то есть, значение, которое даже не было назначено. Мы поговорим о переменных позже, но в JavaScript можно объявить переменную без присвоения ей значения. В этом случае тип переменной будет `"undefined"`.

Ещё в JavaScript есть логический (булевый) тип данных, который может принимать два возможных значения true или false (оба являются ключевыми словами). Любое значение может быть преобразовано в логическое значение в соответствии со следующими правилами:

1. `false`, `0`, пустая строка (`""`), `NaN`, `null` и `undefined` преобразуются в `false.`
2. Все остальные значения преобразуются в `true.`

Преобразование значений можно осуществить явно, используя функцию `Boolean()`:

```js
Boolean(""); // false
Boolean(234); // true
```

Этот метод используется редко, так как JavaScript может автоматически преобразовывать типы в тех случаях, когда ожидается булевое значение, например в операторе `if`. Из-за того, что любой тип данных может быть преобразован в булевое значение, иногда говорят, что данные "истинные" или "ложные".

Для операций с логическими данными используются логические операторы: `&&` (логическое *И*), `||` (логическое *ИЛИ*), `!` (логическое *НЕ*).

Переменные
----------

Для объявления новых переменных в JavaScript используются ключевые слова `let`, `const` или `var`.

```js
let a;
let name = "Simon";
```

**`let`** позволяет объявлять переменные, которые доступны только в ***блоке**,* в котором они объявлены:

```js
// myLetVariable недоступна здесь

for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
  // myLetVariable доступна только здесь
}

// myLetVariable недоступна здесь
```

**`const`** позволяет создавать переменные, чьи значения не предполагают изменений. Переменная доступна из ***блока***, в котором она объявлена.

```js
const Pi = 3.14; // в переменную Pi записано значение.
Pi = 1; // вызовет исключение, так как значение константы нельзя изменить.
```

**`var`** наиболее общее средство объявления переменной. Оно не имеет ограничений, которые имеют два вышеописанных способа. Это потому, что это был изначально единственный способ объявления переменной в JavaScript. Переменная, объявленная с помощью **`var`,** доступна в пределах ***функции***, в которой она объявлена.

```js
var a;
var name = "Simon";
```

Пример кода с переменной, объявленной с помощью **`var`:**

```js
// myVarVariable доступна здесь

for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
  // myVarVariable доступна для всей функции
}

// myVarVariable доступна и здесь
```

Если вы объявляете переменную без присвоения ей какого-либо значения, то её тип будет определён как `undefined`.

Важной особенностью языка JavaScript является то, что блоки данных не имеют своей области видимости, она есть только у функций. Поэтому, если объявить переменную через `var` в блоке данных (например, внутри контролирующей структуры `if`), то она будет доступна всей функции. Следует отметить, что в новом стандарте ECMAScript Edition 6 появились инструкции `let` и `const`, позволяющие объявлять переменные с областью видимости, ограниченной пределами блока.

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

JavaScript поддерживает такие операторы, как `+`, `-`, `*`, `/` и `%`, который возвращает остаток от деления (не путать с модулем (en-US)). Значения присваиваются с помощью оператора `=`, или с помощью составных операторов `+=` и `-=`. Это сокращённая запись выражения `x = x оператор y`.

```js
x += 5;
x = x + 5;
```

Так же используются операторы инкремента (`++`) и декремента (`--`). Которые имеют префиксную и постфиксную форму записи.

Оператор `+` (en-US) так же выполняет конкатенацию (объединение) строк:

```js
"hello" + " world"; // "hello world"
```

При сложении строкового и числового значений происходит автоматическое преобразование в строку. Поначалу такое может запутать:

```js
"3" + 4 + 5; // "345"
3 + 4 + "5"; // "75"
```

Для приведения значения к строке просто прибавьте к нему пустую строку.

Для сравнения (en-US) в JavaScript используются следующие операторы: `<`, `>`, `<=` и `>=`. Сравнивать можно не только числа, но и строки. Проверка на равенство немного сложнее. Для проверки используют двойной (`==`) или тройной (`===`) оператор присваивания. Двойной оператор `==` осуществляет автоматическое преобразование типов, что может приводить к интересным результатам:

```js
123 == "123"; // true
1 == true; // true
```

Если преобразование нежелательно, то используют оператор строгого равенства:

```js
1 === true; // false
123 === "123"; // false
true === true; // true
```

Для проверки на неравенство используют операторы `!=` и `!==`.

Отдельного внимания стоят побитовые операторы (en-US), с которыми вы можете ознакомиться в соответствующем разделе.

Управляющие структуры
---------------------

Управляющие структуры в JavaScript очень похожи на таковые в языках семейства C. Условные операторы выражены ключевыми словами `if` и `else`, которые можно составлять в цепочки:

```js
var name = "kittens";
if (name == "puppies") {
  name += "!";
} else if (name == "kittens") {
  name += "!!";
} else {
  name = "!" + name;
}
name == "kittens!!";
```

В JavaScript есть три типа циклов: `while`, `do-while` и `for`. While используется для задания обычного цикла, а do-while целесообразно применить в том случае, если вы хотите, чтобы цикл был выполнен хотя бы один раз:

```js
while (true) {
  // бесконечный цикл!
}

var input;
do {
  input = get\_input();
} while (inputIsNotValid(input));
```

Цикл `for` похож на такой же в языках C и Java: он позволяет задавать данные для контроля за выполнением цикла:

```js
for (var i = 0; i < 5; i++) {
  // Выполнится 5 раз
}
```

JavaScript также содержит две других известных конструкции: `for...of`

```js
for (let value of array) {
  // операции с value
}
```

и `for...in`:

```js
for (let property in object) {
  // операции над свойствами объекта
}
```

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

```js
var name = o && o.getName();
```

Таким способом удобно задавать значения по умолчанию:

```js
var name = otherName || "default";
```

К условным операторам в JavaScript принадлежит также тернарный оператор "`?`" :

```js
var allowed = age > 18 ? "yes" : "no";
```

Оператор `switch` используется при необходимости множественного сравнения:

```js
switch (action) {
  case "draw":
    drawit();
    break;
  case "eat":
    eatit();
    break;
  default:
    donothing();
}
```

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

```js
switch (a) {
  case 1: // fallthrough
  case 2:
    eatit();
    break;
  default:
    donothing();
}
```

Вариант `default` опциональный. Допускается использование выражений как в условии `switch`, так и в `cases`. При проверке на равенство используется оператор строгого равенства `===`:

```js
switch (1 + 3) {
  case 2 + 2:
    yay();
    break;
  default:
    neverhappens();
}
```

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

Объекты в JavaScript представляют собой коллекции пар имя-значение (ключ-значение). Они похожи на:

* Словари в Python.
* Хеши в Perl и Ruby.
* Таблицы хешей в C и C++.
* HashMaps в Java.
* Ассоциативные массивы в PHP.

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

Существует два основных способа создать объект:

```js
var obj = new Object();
```

А также:

```js
var obj = {};
```

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

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

```js
var obj = {
  name: "Carrot",
  for: "Max",
  details: {
    color: "orange",
    size: 12,
  },
};
```

Доступ к свойствам объекта можно получить следующими способами:

```js
obj.details.color; // orange
obj["details"]["size"]; // 12
```

Эти два метода равнозначны. Первый метод используется, если мы точно знаем к какому методу нам нужно обратиться. Второй метод принимает в качестве имени свойства строку, и позволяет вычислять имя в процессе вычислений. Следует отметить, что последний метод мешает некоторым движкам и минимизаторам оптимизировать код. Если появится необходимость назначить в качестве имён свойств объекта зарезервированные слова, то данный метод тоже может пригодиться:

```js
// Вызовет Syntax error, ведь 'for' это зарезервированное слово
obj.for = "Simon";

// А тут всё нормально
obj["for"] = "Simon";
```

**Примечание:** Стандарт EcmaScript 5 позволяет использовать зарезервированные слова в качестве имён свойств объекта без "оборачивания" их в кавычки. Подробнее в спецификации ES5.

Больше информации об объектах и прототипах: Object.prototype (en-US).

Для получения информации по прототипам объектов и цепям прототипов объектов смотрите Inheritance and the prototype chain.

**Примечание:** Начиная с ECMAScript 2015, ключи объектов могут быть определены переменными с использованием квадратных скобок при создании: `{[phoneType]: 12345}` допустимо вместо `var userPhone = {}; userPhone[phoneType] = 12345`.

Массивы
-------

Массивы в JavaScript всего лишь частный случай объектов. Работают они практически одинаково (если именем свойства является число, то доступ к нему можно получить только через вызов в скобках []), только у массивов есть одно удивительное свойство `length` (длина). Оно возвращает число, равное самому большому индексу массива + 1.

Создать массив можно по старинке:

```js
var a = new Array();
a[0] = "dog";
a[1] = "cat";
a[2] = "hen";
a.length; // 3
```

Но гораздо удобнее использовать литерал массива:

```js
var a = ["dog", "cat", "hen"];
a.length; // 3
```

Запомните, свойство `array.length` не обязательно будет показывать количество элементов в массиве. Посмотрите пример:

```js
var a = ["dog", "cat", "hen"];
a[100] = "fox";
a.length; // 101
```

Запомните — длина массива это его самый большой индекс плюс один.

Если попытаться получить доступ к несуществующему элементу массива, то получите `undefined`:

```js
typeof a[90]; // undefined
```

Для перебора элементов массива используйте такой способ:

```js
for (var i = 0; i < a.length; i++) {
  // Сделать что-нибудь с элементом a[i]
}
```

ES2015 представляет более краткий `for...of` способ обхода по итерируемым объектам, в т.ч. массивам:
```

for (const currentValue of a) {
  // Сделать что-нибудь с currentValue
}
```

Перебрать элементы массива также можно с помощью цикла `for...in`. Но, если вдруг будет изменено какое-либо свойство `Array.prototype`, то оно тоже будет участвовать в выборке. Не используйте данный метод.

И самый новый способ перебора свойств массива был добавлен в ECMAScript 5 — это метод forEach():

```js
["dog", "cat", "hen"].forEach(function (currentValue, index, array) {
  // Сделать что-нибудь с currentValue или array[index]
});
```

Для добавления данных в массив используйте метод `push()`:

```js
a.push(item);
```

У массивов есть ещё множество полезных методов. С их полным списком вы можете ознакомиться по ссылке.

| Метод | Описание |
| --- | --- |
| `a.toString()` | Возвращает строковое представление массива, где все элементы разделены запятыми. |
| `a.toLocaleString()` | Возвращает строковое представление массива в соответствии с выбранной локалью. |
| `a.concat(item1[, item2[, ...[, itemN]]])` | Возвращает новый массив с добавлением указанных элементов. |
| `a.join(sep)` | Преобразует массив в строку, где в качестве разделителя используется параметр `sep` |
| `a.pop()` | Удаляет последний элемент массива и возвращает его. |
| `a.push(item1, ..., itemN)` | Добавляет один или более элементов в конец массива. |
| `a.reverse()` | Меняет порядок элементов массива на обратный. |
| `a.shift()` | Удаляет первый элемент массива и возвращает его. |
| `a.slice(start[, end])` | Возвращает новый массив. |
| `a.sort([cmpfn])` | Сортирует данные в массиве. |
| `a.splice(start, delcount[, item1[, ...[, itemN]]])` | Позволяет вырезать из массива его часть и добавлять на это место новые элементы. |
| `a.unshift(item1[, item2[, ...[, itemN]]])` | Добавляет элементы в начало массива. |

Функции
-------

Наряду с объектами функции также являются ключевыми компонентами языка JavaScript. Базовые функции очень просты:

```js
function add(x, y) {
  var total = x + y;
  return total;
}
```

В этом примере показано практически всё, что нужно знать о функциях. Функции в JavaScript могут принимать ноль или более параметров. Тело функции может содержать любые выражения и определять свои собственные переменные, которые будут для этой функции локальными. Инструкция `return` используется для возврата значения и остановки выполнения функции. Если инструкции return в функции нет (или есть, но не указано возвращаемое значение), то JavaScript возвратит `undefined`.

Можно вызвать функцию, вообще не передавая ей параметры. В таком случае будет считаться, что их значения равны `undefined`:

```js
add(); // NaN
// Нельзя проводить сложение undefined и undefined
```

Можно передать больше аргументов, чем ожидает функция:

```js
add(2, 3, 4); // 5
// используются только первые два аргумента, "4" игнорируется
```

Это может показаться бессмысленным, но на самом деле функции могут получить доступ к "лишним" аргументам с помощью псевдомассива `arguments`, в нём содержатся значения всех аргументов, переданных функции. Давайте напишем функцию, которая принимает неограниченное количество аргументов:

```js
function add() {
  var sum = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    sum += arguments[i];
  }
  return sum;
}

add(2, 3, 4, 5); // 14
```

Или создадим функцию для вычисления среднего значения:

```js
function avg() {
  var sum = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    sum += arguments[i];
  }
  return sum / arguments.length;
}
avg(2, 3, 4, 5); // 3.5
```

Это довольно полезно, но при этом кажется излишне подробным. Для уменьшения количества кода взглянем на замену использования массива аргументов синтаксисом остаточных параметров. В этом случае мы можем передавать в функцию любое количество аргументов, сохраняя код минималистичным. **Оператор остаточных параметров** используется в списке параметров функции в формате: **...variable** и включает в себя целый список аргументов, с которыми функция будет вызвана. Мы будем также использовать замену цикла **for** циклом **for...of** для получения значений, которые будет содержать наша переменная.

```js
function avg(...args) {
  var sum = 0;
  for (let value of args) {
    sum += value;
  }
  return sum / args.length;
}

avg(2, 3, 4, 5); // 3.5
```

**Примечание:** В вышенаписанном коде переменная **args** содержит все значения, которые были переданы в функцию.

Важно отметить, что где бы ни был размещён *rest parameter operator* в объявлении функции, он будет содержать все аргументы *после* его объявления, не раньше. например: *function avg(**firstValue,** ...args)* будет хранить первое переданное значение в переменной **firstValue** и оставшиеся в **args**. Это ещё одно полезное свойство языка, однако оно ведёт нас к новой проблеме. `avg()` функция принимает список аргументов, разделённый запятыми. Но что если вы хотите найти среднее значение в массиве? Вы можете переписать функцию следующим образом:

```js
function avgArray(arr) {
  var sum = 0;
  for (var i = 0, j = arr.length; i < j; i++) {
    sum += arr[i];
  }
  return sum / arr.length;
}

avgArray([2, 3, 4, 5]); // 3.5
```

На тот случай, если вы хотите использовать первый вариант функции, а не переписывать её заново, то в JavaScript есть возможность вызывать функцию с произвольным массивом аргументов. Для этого используется метод `apply():`

```js
avg.apply(null, [2, 3, 4, 5]); // 3.5
```

Вторым аргументом метода `apply()` передаётся массив, который будет передан функции в качестве аргументов. О первом аргументе мы поговорим позже. Наличие у функций методов также говорит о том, что на самом деле они являются объектами.

**Примечание:** Этот же результат можно получить, используя spread operator в вызове функции.

For instance: `avg(...numbers)`

В JavaScript можно создавать анонимные функции:

```js
var avg = function () {
  var sum = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    sum += arguments[i];
  }
  return sum / arguments.length;
};
```

Данная запись семантически равнозначна записи `function` `avg()`. Это даёт возможность использовать разные интересные трюки. Вот посмотрите, как можно "спрятать" локальные переменные в функции:

```js
var a = 1;
var b = 2;
(function () {
  var b = 3;
  a += b;
})();
a; // 4
b; // 2
```

В JavaScript есть возможность рекурсивного вызова функции. Это может оказаться полезным при работе с иерархическими (древовидными) структурами данных (например такие, которые встречаются при работе с DOM).

```js
function countChars(elm) {
  if (elm.nodeType == 3) {
    // TEXT\_NODE
    return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; (child = elm.childNodes[i]); i++) {
    count += countChars(child);
  }
  return count;
}
```

Тут мы сталкиваемся с проблемой: как вызвать функцию рекурсивно, если у неё нет имени? Для этого в JavaScript есть именованные функциональные выражения IIFEs (Immediately Invoked Function Expressions). Вот пример использования именованной самовызывающейся функции:

```js
var charsInBody = (function counter(elm) {
  if (elm.nodeType == 3) {
    // TEXT\_NODE
    return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; (child = elm.childNodes[i]); i++) {
    count += counter(child);
  }
  return count;
})(document.body);
```

Имя функции в примере доступно только внутри самой функции. Это улучшает оптимизацию и читаемость кода.

Собственные объекты
-------------------

**Примечание:** Для более подробной информации по объектно-ориентированному программированию в JavaScript смотрите Введение в объектно-ориентированный JavaScript.

В классическом Объектно-Ориентированном Программировании (ООП) объекты — это коллекции данных и методов, которые этими данными оперируют. JavaScript - это язык, основанный на прототипах, и в его определении нет понятия классов, таких, как в языках C++ или Java. (Иногда это может запутать программистов, знакомых с языками, в которых есть классы.) Вместо классов JavaScript использует функции. Давайте представим объект с личными данными, содержащий поля с именем и фамилией. Есть два типа отображения имён: "Имя Фамилия" или "Фамилия, Имя". С помощью объектов и функций можно сделать следующее:

```js
function makePerson(first, last) {
  return {
    first: first,
    last: last,
  };
}

function personFullName(person) {
  return person.first + " " + person.last;
}

function personFullNameReversed(person) {
  return person.last + ", " + person.first;
}

s = makePerson("Simon", "Willison");
personFullName(s); // Simon Willison
personFullNameReversed(s); // Willison, Simon
```

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

```js
function makePerson(first, last) {
  return {
    first: first,
    last: last,
    fullName: function () {
      return this.first + " " + this.last;
    },
    fullNameReversed: function () {
      return this.last + ", " + this.first;
    },
  };
}
s = makePerson("Simon", "Willison");
s.fullName(); // Simon Willison
s.fullNameReversed(); // Willison, Simon
```

А вот кое-что новенькое: ключевое слово `this`. Когда `this` используется внутри функции, оно ссылается на текущий объект. Значение ключевого слова зависит от способа вызова функции. Если вызвать функцию с обращением к объекту через точку или квадратные скобки, то `this` получится равным данному объекту. В ином случае `this` будет ссылаться на глобальный объект. Это часто приводит к ошибкам. Например:

```js
s = makePerson("Simon", "Willison");
var fullName = s.fullName;
fullName(); // undefined undefined
```

При вызове `fullName()`, `this` получает ссылку на глобальный объект. А так как в глобальном объекте не определены переменные `first` и `last`, то имеем два `undefined`.

Используя особенность ключевого слова `this`, можно улучшить код функции `makePerson`:

```js
function Person(first, last) {
  this.first = first;
  this.last = last;
  this.fullName = function () {
    return this.first + " " + this.last;
  };
  this.fullNameReversed = function () {
    return this.last + ", " + this.first;
  };
}
var s = new Person("Simon", "Willison");
```

В примере мы использовали новое ключевое слово: `new`. Оно тесно связано с this. Данное ключевое слово создаёт новый пустой объект, а потом вызывает указанную функцию, а `this` получает ссылку на этот новый объект. Функции, которые предназначены для вызова с `new` называются конструкторами. Существует соглашение, согласно которому все функции-конструкторы записываются с заглавной буквы.

Мы доработали наш код в предыдущем примере, но всё равно остался один неприятный момент с самостоятельным вызовом `fullName()`.

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

```js
function personFullName() {
  return this.first + " " + this.last;
}
function personFullNameReversed() {
  return this.last + ", " + this.first;
}
function Person(first, last) {
  this.first = first;
  this.last = last;
  this.fullName = personFullName;
  this.fullNameReversed = personFullNameReversed;
}
```

Уже лучше: мы создали функции-методы только один раз, а при новом вызове функции-конструктора просто ссылаемся на них. Можно сделать ещё лучше? Конечно:

```js
function Person(first, last) {
  this.first = first;
  this.last = last;
}
Person.prototype.fullName = function fullName() {
  return this.first + " " + this.last;
};
Person.prototype.fullNameReversed = function fullNameReversed() {
  return this.last + ", " + this.first;
};
```

`Person.prototype` это объект, доступ к которому есть у всех экземпляров класса `Person`. Он создаёт особую цепочку прототипов. Каждый раз, когда вы пытаетесь получить доступ к несуществующему свойству объекта `Person`, JavaScript проверяет, существует ли свойство в `Person.prototype`. В результате все, что передано в `Person.prototype`, становится доступным и всем экземплярам этого конструктора через `this` объект.

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

```js
s = new Person("Simon", "Willison");
s.firstNameCaps();
// TypeError on line 1: s.firstNameCaps is not a function

Person.prototype.firstNameCaps = function () {
  return this.first.toUpperCase();
};
s.firstNameCaps(); // "SIMON"
```

Занимательно то, что добавлять свойства в прототип можно и для встроенных объектов JavaScript. Давайте добавим новый метод `reversed` классу `String`, этот метод будет возвращать строку задом наперёд:

```js
var s = "Simon";
s.reversed(); // TypeError on line 1: s.reversed is not a function

String.prototype.reversed = function reversed() {
  var r = "";
  for (var i = this.length - 1; i >= 0; i--) {
    r += this[i];
  }
  return r;
};
s.reversed(); // "nomiS"
```

Данный метод будет работать даже на литералах строки!

```js
"This can now be reversed".reversed();
// desrever eb won nac sihT
```

Как уже упоминалось, prototype формирует часть цепочки. Конечным объектом этой цепочки прототипов является `Object.prototype`, методы которого включают и `toString()` — тот метод, который вызывается тогда, когда надо получить строковое отображение объекта. Вот что можно сделать с нашими объектами `Person`:

```js
var s = new Person("Simon", "Willison");
s.toString(); // [object Object]

Person.prototype.toString = function () {
  return "<Person: " + this.fullName() + ">";
};

s.toString(); // "<Person: Simon Willison>"
```

Помните, мы вызывали `avg.apply()` с первым аргументом равным null? Теперь мы можем сделать так: первым аргументом, переданным методу `apply()` будет объект, который примет значение `this`. Вот к примеру упрощённая реализация `new`:

```js
function trivialNew(constructor, ...args) {
  var o = {}; // Создаём новый объект
  constructor.apply(o, args);
  return o;
}
```

Это не точная копия `new`, так как она не устанавливает цепочку прототипов (это сложно ). Метод `apply()` применяется не очень часто, но знать его важно. В примере выше, синтаксис `...args` (включая многоточие) называется остаточными параметрами, потому что они включают в себя все оставшиеся аргументы.

Вызов

```js
var bill = trivialNew(Person, "William", "Orange");
```

практически полностью эквивалентен этому:

```js
var bill = new Person("William", "Orange");
```

В JavaScript метод `apply()` имеет похожий метод `call()`, который тоже позволяет устанавливать `this`, но принимает список, а не массив аргументов.

```js
function lastNameCaps() {
  return this.last.toUpperCase();
}
var s = new Person("Simon", "Willison");
lastNameCaps.call(s);
// Аналогично записи:
s.lastNameCaps = lastNameCaps;
s.lastNameCaps(); // WILLISON
```

Вложенные функции
-----------------

Объявлять новые функции можно и внутри других функций. Мы использовали этот приём чуть выше, создавая функцию `makePerson()`. Главная особенность вложенных функций в том, что они получают доступ к переменным, объявленным в их функции-родителе:

```js
function parentFunc() {
  var a = 1;

  function nestedFunc() {
    var b = 4; // parentFunc can't use this
    return a + b;
  }
  return nestedFunc(); // 5
}
```

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

Ещё это отличный способ сократить количество глобальных переменных. Так при написании кода у нас часто будет возникать искушение понасоздавать глобальных переменных, которые будут доступны разным функциям. Всё это усложняет код, делает его менее читаемым. Вложенные функции имеют доступ к переменным своей функции-родителя, и мы можем использовать это для группировки множества функций вместе (естественно в разумных пределах), что позволит держать наш глобальный объект в чистоте и порядке.

Замыкания (Closures)
--------------------

Мы подошли к одному из самых мощных и непонятных инструментов JavaScript. Давайте разберёмся.

```js
function makeAdder(a) {
  return function (b) {
    return a + b;
  };
}

var x = makeAdder(5);
var y = makeAdder(20);
x(6); // ?
y(7); // ?
```

Функция `makeAdder` создаёт новую функцию, которая прибавляет полученное значение к значению, которые было получено при создании функции.

Такой же фокус мы наблюдали в предыдущем примере, когда внутренние функции получали доступ к переменным той функции, в которой были объявлены. Только в нашем примере основная функция возвращает вложенную. Поначалу может показаться, что локальные переменные при этом перестанут существовать. Но они продолжают существовать — иначе код попросту не сработал бы. Вдобавок ко всему у нас есть две разные "копии" функции `makeAdder`, присвоенные разным переменным (одна копия, в которой `а` - это 5, а во второй `а` - это 20). Вот что имеем в результате вызова:

```js
x(6); // возвратит 11
y(7); // возвратит 27
```

И вот что произошло: когда JavaScript выполняет функцию, создаётся объект 'scope', который содержит в себе все локальные переменные, объявленные внутри этой функции. Он инициализируется любым значением, переданным функции в качестве параметра. 'Scope' подобен глобальному объекту, который содержит все глобальные переменные и функции, кроме нескольких важных отличий: при каждом вызове функции создаётся новый объект 'scope' и, в отличие от глобального, к объекту 'scope' нельзя получить прямой доступ из вашего кода. И нет способа пройтись по свойствам данного объекта.

Так что при вызове функции `makeAdder` создаётся новый объект 'scope' с единственным свойством: `a`, которому присваивается значение, переданное функции в качестве аргумента. Потом `makeAdder` возвращает новую анонимную функцию. В любом другом случае 'сборщик мусора' удалил бы объект scope, но возвращаемая функция ссылается на этот объект. В итоге объект scope не удаляется до тех пор, пока существует хотя бы одна ссылка на него.

Все объекты scope соединяются в цепочку областей видимости, которая похожа на цепочку прототипов в объектной системе JavaScript.

**Замыкание** это связка из функции и объекта scope, созданного при её вызове. Подробнее о замыканиях здесь.


Замыкания - JavaScript

Замыкания
=========

Замыкание — это комбинация функции и лексического окружения, в котором эта функция была определена. Другими словами, замыкание даёт вам доступ к Scope (en-US) внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз при создании функции, во время её создания.

Лексическая область видимости
-----------------------------

Рассмотрим следующий пример:

```js
function init() {
  var name = "Mozilla"; // name - локальная переменная, созданная в init
  function displayName() {
    // displayName() - внутренняя функция, замыкание
    alert(name); // displayName() использует переменную, объявленную в родительской функции
  }
  displayName();
}
init();
```

`init()` создаёт локальную переменную `name` и определяет функцию `displayName()`. `displayName()` — это внутренняя функция — она определена внутри `init()` и доступна только внутри тела функции `init()`. Обратите внимание, что функция `displayName()` не имеет никаких собственных локальных переменных. Однако, поскольку внутренние функции имеют доступ к переменным внешних функций, `displayName()` может иметь доступ к переменной `name`, объявленной в родительской функции `init()`.

Выполните этот код и обратите внимание, что команда `alert()` внутри `displayName()` благополучно выводит на экран содержимое переменной `name` объявленной в родительской функции. Это пример так называемой лексической области видимости *(lexical* *scoping)*: в JavaScript область действия переменной определяется по её расположению в коде (это очевидно *лексически*), и вложенные функции имеют доступ к переменным, объявленным вовне. Этот механизм и называется Lexical scoping (область действия, ограниченная лексически).

Замыкание
---------

Рассмотрим следующий пример:

```js
function makeFunc() {
  var name = "Mozilla";

  function displayName() {
    alert(name);
  }

  return displayName;
}

var myFunc = makeFunc();
myFunc();
```

Если выполнить этот код, то результат будет такой же, как и выполнение `init()` из предыдущего примера: строка "Mozilla" будет показана в JavaScript alert диалоге. Что отличает этот код и представляет для нас интерес, так это то, что внутренняя функция `displayName()` была возвращена из внешней до того, как была выполнена.

На первый взгляд, кажется неочевидным, что этот код правильный, но он работает. В некоторых языках программирования локальные переменные-функции существуют только во время выполнения этой функции. После завершения выполнения `makeFunc()` можно ожидать, что переменная *name* больше не будет доступна. Однако, поскольку код продолжает нормально работать, очевидно, что это не так в случае JavaScript.

Причина в том, что функции в JavaScript формируют так называемые *замыкания*. *Замыкание* — это комбинация функции и лексического окружения, в котором эта функция была объявлена. Это окружение состоит из произвольного количества локальных переменных, которые были в области действия функции во время создания замыкания. В рассмотренном примере `myFunc` — это ссылка на экземпляр функции `displayName`, созданной в результате выполнения `makeFunc`. Экземпляр функции `displayName` в свою очередь сохраняет ссылку на своё лексическое окружение, в котором есть переменная `name`. По этой причине, когда происходит вызов функции `myFunc`, переменная `name` остаётся доступной для использования и сохранённый в ней текст "Mozilla" передаётся в `alert`.

А вот немного более интересный пример — функция `makeAdder`:

```js
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
```

Здесь мы определили функцию `makeAdder(x)`, которая получает единственный аргумент `x` и возвращает новую функцию. Эта функция получает единственный аргумент `y` и возвращает сумму `x` и `y`.

По существу `makeAdder` — это фабрика функций: она создаёт функции, которые могут прибавлять определённое значение к своему аргументу. В примере выше мы используем нашу фабричную функцию для создания двух новых функций — одна прибавляет 5 к своему аргументу, вторая прибавляет 10.

`add5` и `add10` — это примеры замыканий. Эти функции делят одно определение тела функции, но при этом они сохраняют различные окружения. В окружении функции `add5` `x` — это 5, в то время как в окружении `add10` `x` — это 10.

Замыкания на практике
---------------------

Замыкания полезны тем, что позволяют связать данные (лексическое окружение) с функцией, которая работает с этими данными. Очевидна параллель с объектно-ориентированным программированием, где объекты позволяют нам связать некоторые данные (свойства объекта) с одним или несколькими методами.

Следовательно, замыкания можно использовать везде, где вы обычно использовали объект с одним единственным методом.

Такие ситуации повсеместно встречаются в web-разработке. Большое количество front-end кода, который мы пишем на JavaScript, основано на обработке событий. Мы описываем какое-то поведение, а потом связываем его с событием, которое создаётся пользователем (например, клик мышкой или нажатие клавиши). При этом наш код обычно привязывается к событию в виде обратного/ответного вызова (callback): *callback функция - функция выполняемая в ответ на возникновение события*.

Давайте рассмотрим практический пример: допустим, мы хотим добавить на страницу несколько кнопок, которые будут менять размер текста. Как вариант, мы можем указать свойство font-size на элементе body в пикселах, а затем устанавливать размер прочих элементов страницы (таких, как заголовки) с использованием относительных единиц em:

```css
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
```

Тогда наши кнопки будут менять свойство font-size элемента body, а остальные элементы страницы просто получат это новое значение и отмасштабируют размер текста благодаря использованию относительных единиц.

Используем следующий JavaScript:

```js
function makeSizer(size) {
  return function () {
    document.body.style.fontSize = size + "px";
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
```

Теперь `size12`, `size14`, и `size16` - это функции, которые меняют размер текста в элементе body на значения 12, 14, и 16 пикселов, соответственно. После чего мы цепляем эти функции на кнопки примерно так:

```js
document.getElementById("size-12").onclick = size12;
document.getElementById("size-14").onclick = size14;
document.getElementById("size-16").onclick = size16;
```

```html
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>
```

Эмуляция частных (private) методов с помощью замыканий
------------------------------------------------------

Языки вроде Java позволяют нам объявлять частные (private) методы . Это значит, что они могут быть вызваны только методами того же класса, в котором объявлены.

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

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

```js
var Counter = (function () {
  var privateCounter = 0;

  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment: function () {
      changeBy(1);
    },
    decrement: function () {
      changeBy(-1);
    },
    value: function () {
      return privateCounter;
    },
  };
})();

alert(Counter.value()); /\* Alerts 0 \*/

Counter.increment();
Counter.increment();

alert(Counter.value()); /\* Alerts 2 \*/

Counter.decrement();

alert(Counter.value()); /\* Alerts 1 \*/
```

Тут много чего поменялось. В предыдущем примере каждое замыкание имело свой собственный контекст исполнения (окружение). Здесь мы создаём единое окружение для трёх функций: `Counter.increment`, `Counter.decrement`, и `Counter.value`.

Единое окружение создаётся в теле анонимной функции, которая исполняется в момент описания. Это окружение содержит два приватных элемента: переменную `privateCounter` и функцию `changeBy(val)`. Ни один из этих элементов не доступен напрямую, за пределами этой самой анонимной функции. Вместо этого они могут и должны использоваться тремя публичными функциями, которые возвращаются анонимным блоком кода (anonymous wrapper), выполняемым в той же анонимной функции.

Эти три публичные функции являются замыканиями, использующими общий контекст исполнения (окружение). Благодаря механизму lexical scoping в Javascript, все они имеют доступ к переменной `privateCounter` и функции `changeBy`.

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

```js
var makeCounter = function () {
  var privateCounter = 0;

  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment: function () {
      changeBy(1);
    },
    decrement: function () {
      changeBy(-1);
    },
    value: function () {
      return privateCounter;
    },
  };
};

var Counter1 = makeCounter();
var Counter2 = makeCounter();

alert(Counter1.value()); /\* Alerts 0 \*/

Counter1.increment();
Counter1.increment();

alert(Counter1.value()); /\* Alerts 2 \*/

Counter1.decrement();

alert(Counter1.value()); /\* Alerts 1 \*/
alert(Counter2.value()); /\* Alerts 0 \*/
```

Заметьте, что счётчики работают независимо друг от друга. Это происходит потому, что у каждого из них в момент создания функцией `makeCounter()` также создавался свой отдельный контекст исполнения (окружение). То есть приватная переменная `privateCounter` в каждом из счётчиков это действительно отдельная, самостоятельная переменная.

Используя замыкания подобным образом, вы получаете ряд преимуществ, обычно ассоциируемых с объектно-ориентированным программированием, таких как изоляция и инкапсуляция.

Создание замыканий в цикле: Очень частая ошибка
-----------------------------------------------

До того, как в версии ECMAScript 6 ввели ключевое слово `let`, постоянно возникала следующая проблема при создании замыканий внутри цикла. Рассмотрим пример:

```html
<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email" /></p>
<p>Name: <input type="text" id="name" name="name" /></p>
<p>Age: <input type="text" id="age" name="age" /></p>
```

```js
function showHelp(help) {
  document.getElementById("help").innerHTML = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Ваш адрес e-mail" },
    { id: "name", help: "Ваше полное имя" },
    { id: "age", help: "Ваш возраст (Вам должно быть больше 16)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function () {
      showHelp(item.help);
    };
  }
}

setupHelp();
```

Массив `helpText` описывает три подсказки для трёх полей ввода. Цикл пробегает эти описания по очереди и для каждого из полей ввода определяет, что при возникновении события `onfocus` для этого элемента должна вызываться функция, показывающая соответствующую подсказку.

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

Проблема в том, что функции, присвоенные как обработчики события `onfocus`, являются замыканиями. Они состоят из описания функции и контекста исполнения (окружения), унаследованного от функции `setupHelp`. Было создано три замыкания, но все они были созданы с одним и тем же контекстом исполнения. К моменту возникновения события `onfocus` цикл уже давно отработал, а значит, переменная `item` (одна и та же для всех трёх замыканий) указывает на последний элемент массива, который как раз в поле возраста.

В качестве решения в этом случае можно предложить использование функции, фабричной функции (function factory), как уже было описано выше в примерах:

```js
function showHelp(help) {
  document.getElementById("help").innerHTML = help;
}

function makeHelpCallback(help) {
  return function () {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Ваш адрес e-mail" },
    { id: "name", help: "Ваше полное имя" },
    { id: "age", help: "Ваш возраст (Вам должно быть больше 16)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();
```

Вот это работает как следует. Вместо того, чтобы делить на всех одно окружение, функция `makeHelpCallback` создаёт каждому из замыканий своё собственное, в котором переменная `item` указывает на правильный элемент массива `helpText`.

Соображения по производительности
---------------------------------

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

Как пример, при написании нового класса есть смысл помещать все методы в прототип его объекта, а не описывать их в тексте конструктора. Если сделать по-другому, то при каждом создании объекта для него будет создан свой экземпляр каждого из методов, вместо того, чтобы наследовать их из прототипа.

Давайте рассмотрим не очень практичный, но показательный пример:

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();

  this.getName = function () {
    return this.name;
  };

  this.getMessage = function () {
    return this.message;
  };
}
```

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

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}

MyObject.prototype = {
  getName: function () {
    return this.name;
  },
  getMessage: function () {
    return this.message;
  },
};
```

Методы вынесены в прототип. Тем не менее, переопределять прототип — само по себе является плохой привычкой, поэтому давайте перепишем всё так, чтобы новые методы просто добавились к уже существующему прототипу.

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}

MyObject.prototype.getName = function () {
  return this.name;
};

MyObject.prototype.getMessage = function () {
  return this.message;
};
```

Код выше можно сделать аккуратнее:

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}

(function () {
  this.getName = function () {
    return this.name;
  };

  this.getMessage = function () {
    return this.message;
  };
}).call(MyObject.prototype);
```

В обоих примерах выше методы определяются один раз — в прототипе. И все объекты, использующие данный прототип, будут использовать это определение без дополнительного расхода вычислительных ресурсов. Смотрите подробное описание в статье Подробнее об объектной модели (en-US).

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


Параллельная модель и цикл событий. - JavaScript

Параллельная модель и цикл событий.
===================================

Параллелизм/Многопоточность в JavaScript работает за счёт цикла событий (**event loop**), который отвечает за выполнение кода, сбора и обработки событий и выполнения под-задач из очереди (**queued sub-tasks**). Эта модель весьма отличается от других языков программирования, таких как C и Java.

Концепция жизненного цикла
--------------------------

В следующей секции объясняется теоретическая модель. Современные JavaScript движки внедряют/имплементируют и существенно оптимизируют этот процесс.

### Визуальное представление

![Stack, heap, queue](/ru/docs/Web/JavaScript/EventLoop/the_javascript_runtime_environment_example.svg)

Для лучшего **визуального** представления работы **Event loop**, Вы можете ознакомиться с данным видео: https://www.youtube.com/watch?v=8aGhZQkoFbQ&t=389s

### Стек

Вызов любой функции создаёт контекст выполнения (Execution Context (en-US)). При вызове вложенной функции создаётся новый контекст, а старый сохраняется в специальной структуре данных - стеке вызовов (Call Stack).

```js
function f(b) {
  var a = 12;
  return a + b + 35;
}

function g(x) {
  var m = 4;
  return f(m \* x);
}

g(21);
```

Когда вызывается функция `g`, создаётся первый контекст выполнения, содержащий аргументы функции `g` и локальные переменные. Когда `g` вызывает `f`, создаётся второй контекст с аргументами `f` и её локальными переменными. И этот контекст выполнения `f` помещается в стек вызовов выше первого. Когда `f` возвращает результат, верхний элемент из стека удаляется. Когда `g` возвращает результат, её контекст также удалится, и стек становится пустым.

### Куча

Объекты размещаются в куче. Куча — это просто имя для обозначения большой неструктурированной области памяти.

### Очередь

Среда выполнения JavaScript содержит очередь задач. Эта очередь — список задач, подлежащих обработке. Каждая задача ассоциируется с некоторой функцией, которая будет вызвана, чтобы обработать эту задачу.

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

Обработка задачи заканчивается, когда стек снова становится пустым. Следующая задача извлекается из очереди и начинается её обработка.

Цикл событий
------------

Модель событийного цикла (`event loop`) называется так потому, что отслеживает новые события в цикле:

```js
while (queue.waitForMessage()) {
  queue.processNextMessage();
}
```

`queue.waitForMessage` ожидает поступления задач, если очередь пуста.

### Запуск до завершения

Каждая задача выполняется полностью, прежде чем начнёт обрабатываться следующая. Благодаря этому мы точно знаем: когда выполняется текущая функция – она не может быть приостановлена и будет целиком завершена до начала выполнения другого кода (который может изменить данные, с которыми работает текущая функция). Это отличает JavaScript от такого языка программирования как C. Поскольку в С функция, запущенная в отдельном потоке, в любой момент может быть остановлена, чтобы выполнить какой-то другой код в другом потоке.

У данного подхода есть и минусы. Если задача занимает слишком много времени, то веб-приложение не может обрабатывать действия пользователя в это время (например, скролл или клик). Браузер старается смягчить проблему и выводит сообщение *"скрипт выполняется слишком долго" ("a script is taking too long to run")* и предлагает остановить его. Хорошей практикой является создание задач, которые исполняются быстро, и если возможно, разбиение одной задачи на несколько мелких.

### Добавление событий в очередь

В браузерах события добавляются в очередь в любое время, если событие произошло, а так же если у него есть обработчик. В случае, если обработчика нет – событие потеряно. Так, клик по элементу, имеющему обработчик события по событию `click`, добавит событие в очередь, а если обработчика нет – то и событие в очередь не попадёт.

Вызов setTimeout добавит событие в очередь по прошествии времени, указанного во втором аргументе вызова. Если очередь событий на тот момент будет пуста, то событие обработается сразу же, в противном случае событию функции `setTimeout` придётся ожидать завершения обработки остальных событий в очереди. Именно поэтому второй аргумент `setTimeout` корректно считать не временем, через которое выполнится функция из первого аргумента, а минимальное время, через которое она сможет выполниться.

### Нулевые задержки

Нулевая задержка не даёт гарантии, что обработчик выполнится через ноль миллисекунд. Вызов `setTimeout` с аргументом 0 (ноль) не завершится за указанное время. Выполнение зависит от количества ожидающих задач в очереди. Например, сообщение ''this is just a message'' из примера ниже будет выведено на консоль раньше, чем произойдёт выполнение обработчика *cb1*. Это произойдёт, потому что задержка – это минимальное время, которое требуется среде выполнения на обработку запроса.

```js
(function () {
  console.log("this is the start");

  setTimeout(function cb() {
    console.log("this is a msg from call back");
  });

  console.log("this is just a message");

  setTimeout(function cb1() {
    console.log("this is a msg from call back1");
  }, 0);

  console.log("this is the end");
})();

// "this is the start"
// "this is just a message"
// "this is the end"
// "this is a msg from call back"
// "this is a msg from call back1"
```

### Связь нескольких потоков между собой

Web Worker или кросс-доменный фрейм имеют свой собственный стек, кучу и очередь событий. Два отдельных событийных потока могут связываться друг с другом, только через отправку сообщений с помощью метода `postMessage`. Этот метод добавляет сообщение в очередь другого, если он конечно принимает их.

Никогда не блокируется
----------------------

Очень интересное свойство цикла событий в JavaScript, что в отличие от множества других языков, поток выполнения никогда не блокируется. Обработка I/O обычно осуществляется с помощью событий и колбэк-функций, поэтому даже когда приложение ожидает запрос от IndexedDB или ответ от XHR, оно может обрабатывать другие процессы, например пользовательский ввод.

Существуют хорошо известные исключения как `alert` или синхронный XHR, но считается хорошей практикой избегать их использования.


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

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

Эта глава описывает все операторы, выражения и ключевые слова языка JavaScript.

Выражения и операторы по категориям
-----------------------------------

Алфавитный список смотрите в боковой панели слева.

### Первичные выражения

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

`this`

Ключевое слово `this` ссылается на контекст выполняемой функции.

`function`

Ключевое слово `function` определяет выражение функции.

`class`

Ключевое слово `class` определяет выражение класса.

`function*`

Ключевое слово `function*` (со звёздочкой) определяет функцию-генератор.

`yield`

Пауза или продолжение выполнения функции-генератора.

`yield*`

Делегирует выполнение другому генератору итерируемому объекту.

Экспериментальная возможность `async function*`

`async function` определяет выражение асинхронной функции.

Экспериментальная возможность `await`

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

`[]`

Синтаксис создания инициализатора/литерала массива.

`{}`

Синтаксис создания инициализатора/литерала объекта

`/ab+c/i`

Синтаксис создания литерала регулярного выражения.

`( )`

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

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

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

Доступ к свойству

 Операторы доступа к членам предоставляют доступ к свойству или методу объекта
 (`object.property` и `object['property']`).

`new`

Оператор `new` создаёт экземпляр объекта с использованием конструктора.

new.target

Свойство `new.target` ссылается на конструктор или функцию, который был вызван через оператор `new`.

`super`

Ключевое слово `super` вызывает конструктор родительского класса.

`...obj`

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

### Инкремент и декремент

Операторы постфиксного/префиксного инкремента и декремента.

`A++` (en-US)

Оператор постфиксного инкремента.

`A--` (en-US)

Оператор постфиксного декремента.

`++A` (en-US)

Оператор префиксного инкремента. *(Значение переменной, сначала, увеличивается на 1, а затем используется в выражении)*

`--A` (en-US)

Оператор префиксного декремента.

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

Унарные операции — это операции с одним операндом.

`delete`

Оператор `delete` удаляет свойство из объекта.

`void`

Оператор `void` отбрасывает возвращаемое значение выражения.

`typeof`

Оператор `typeof` определяет тип переданного объекта.

`+` (en-US)

Оператор унарного плюса преобразует свой операнд в тип `Number`.

`-` (en-US)

Оператор унарного минуса преобразует свой операнд в тип `Number`, а затем меняет его знак.

`~` (en-US)

Оператор побитового НЕ.

`!` (en-US)

Оператор логического НЕ.

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

Арифметические операторы своими операндами принимают числовые значения (литералы или переменные) и возвращают одно числовое значение.

`+` (en-US)

Оператор сложения.

`-` (en-US)

Оператор вычитания.

`/` (en-US)

Оператор деления.

`*` (en-US)

Оператор умножения.

`%` (en-US)

Оператор взятия остатка от деления.

`**` (en-US)

Оператор возведения в степень.

### Реляционные операторы

Реляционные операторы (операторы отношения) сравнивают свои операнды и возвращают значение типа `Boolean`, зависящее от того, был ли результат сравнения истинным или ложным.

`in`

Оператор `in` определяет, содержит ли объект указанное свойство.

`instanceof`

Оператор `instanceof` определяет, является ли объект экземпляром потомком Prototype (или экземпляром) другого объекта.

`<` (en-US)

Оператор меньше.

`>` (en-US)

Оператор больше.

`<=` (en-US)

Оператор меньше или равно.

`>=` (en-US)

Оператор больше или равно.

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

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

Результат вычисления оператора равенства всегда имеет тип `Boolean` и значение, зависящее от того, был ли результат проверки истинным или ложным.

`==` (en-US)

Оператор проверки на равенство.

`!=` (en-US)

Оператор проверки на неравенство.

`===` (en-US)

Оператор проверки на идентичность.

`!==` (en-US)

Оператор проверки на неидентичность.

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

Операции для сдвига всех битов операнда.

`<<` (en-US)

Оператор побитового сдвига влево.

`>>` (en-US)

Оператор знакового побитового сдвига вправо.

`>>>` (en-US)

Оператор беззнакового побитового сдвига вправо.

### Бинарные побитовые операторы

Побитовые операторы трактуют свои операнды как набор из 32 битов (нулей и единиц) и возвращают стандартные числовые значения JavaScript.

`&` (en-US)

Оператор побитового И.

`|` (en-US)

Оператор побитового ИЛИ.

`^` (en-US)

Оператор побитового ИСКЛЮЧАЮЩЕГО ИЛИ.

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

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

`&&` (en-US)

Оператор логического И.

`||` (en-US)

Оператор логического ИЛИ.

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

`(условие ? ifTrue : ifFalse)`

Условный оператор возвращает одно значение из двух данных в зависимости от логического значения условия.

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

Операторы присваивания присваивают значение своему левому операнду, зависящее от значения их правого операнда.

`=` (en-US)

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

`*=` (en-US)

Оператор присваивания с умножением.

`/=` (en-US)

Оператор присваивания с делением.

`%=` (en-US)

Оператор присваивания с взятием остатка от деления.

`+=` (en-US)

Оператор присваивания со сложением.

`-=` (en-US)

Оператор присваивания с вычитанием.

`<<=` (en-US)

Оператор присваивания со сдвигом влево.

`>>=` (en-US)

Оператор присваивания со знаковым сдвигом вправо.

`>>>=` (en-US)

Оператор присваивания с беззнаковым сдвигом вправо.

`&=` (en-US)

Оператор присваивания с побитовым И.

`^=` (en-US)

Оператор присваивания с побитовым ИСКЛЮЧАЮЩИМ ИЛИ.

`|=` (en-US)

Оператор присваивания с побитовым ИЛИ.

`[a, b] = [1, 2]`
`{a, b} = {a:1, b:2}`

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

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

`,`

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

### Нестандартные особенности

Non-standard Legacy generator function (en-US)

Ключевое слово `function` может быть использовано для определения устаревших функций-генераторов в одном выражении. Для того, чтобы сделать такую функцию, её тело должно содержать хотя бы одно выражение `yield`.

Non-standard Expression closures

Синтаксис закрытого выражения для сокращённого написания простейших функций.

Non-standard `[for (x of y) x]` (en-US)

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

Non-standard `(for (x of y) y)` (en-US)

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

Спецификации
------------

| Specification |
| --- |
| ECMAScript Language Specification # sec-addition-operator-plus |
| ECMAScript Language Specification # sec-assignment-operators |
| ECMAScript Language Specification # sec-async-function-definitions |
| ECMAScript Language Specification # sec-async-generator-function-definitions |
| ECMAScript Language Specification # prod-BitwiseANDExpression |
| ECMAScript Language Specification # sec-bitwise-not-operator |
| ECMAScript Language Specification # prod-BitwiseORExpression |
| ECMAScript Language Specification # prod-BitwiseXORExpression |
| ECMAScript Language Specification # sec-class-definitions |
| ECMAScript Language Specification # sec-comma-operator |
| ECMAScript Language Specification # sec-conditional-operator |
| ECMAScript Language Specification # sec-postfix-decrement-operator |
| ECMAScript Language Specification # sec-delete-operator |
| ECMAScript Language Specification # sec-destructuring-assignment |
| ECMAScript Language Specification # sec-destructuring-binding-patterns |
| ECMAScript Language Specification # sec-multiplicative-operators |
| ECMAScript Language Specification # sec-equality-operators |
| ECMAScript Language Specification # sec-exp-operator |
| ECMAScript Language Specification # sec-function-definitions |
| ECMAScript Language Specification # sec-generator-function-definitions |
| ECMAScript Language Specification # sec-relational-operators |
| ECMAScript Language Specification # sec-grouping-operator |
| ECMAScript Language Specification # sec-import-calls |
| ECMAScript Language Specification # prod-ImportMeta |
| HTML Standard # hostgetimportmetaproperties |
| ECMAScript Language Specification # sec-postfix-increment-operator |
| ECMAScript Language Specification # sec-left-shift-operator |
| ECMAScript Language Specification # prod-LogicalANDExpression |
| ECMAScript Language Specification # sec-logical-not-operator |
| ECMAScript Language Specification # prod-LogicalORExpression |
| ECMAScript Language Specification # sec-new-operator |
| ECMAScript Language Specification # sec-built-in-function-objects |
| ECMAScript Language Specification # sec-null-value |
| ECMAScript Language Specification # prod-CoalesceExpression |
| ECMAScript Language Specification # sec-object-initializer |
| ECMAScript Language Specification # prod-OptionalExpression |
| ECMAScript Language Specification # sec-property-accessors |
| ECMAScript Language Specification # sec-signed-right-shift-operator |
| ECMAScript Language Specification # prod-SpreadElement |
| ECMAScript Language Specification # prod-ArgumentList |
| ECMAScript Language Specification # prod-PropertyDefinition |
| ECMAScript Language Specification # sec-subtraction-operator-minus |
| ECMAScript Language Specification # sec-super-keyword |
| ECMAScript Language Specification # sec-this-keyword |
| ECMAScript Language Specification # sec-typeof-operator |
| ECMAScript Language Specification # sec-unary-minus-operator |
| ECMAScript Language Specification # sec-unary-plus-operator |
| ECMAScript Language Specification # sec-unsigned-right-shift-operator |
| ECMAScript Language Specification # sec-void-operator |
| ECMAScript Language Specification # prod-YieldExpression |
| ECMAScript Language Specification # sec-generator-function-definitions-runtime-semantics-evaluation |

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

* Приоритеты операторов


Инструкции и объявления - JavaScript

Инструкции и объявления
=======================

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

Инструкции и объявления по категориям
-------------------------------------

Алфавитный список смотрите в боковой панели слева.

### Управление потоком выполнения

Блок

Блок используется для группировки нуля и более инструкций. Блок отделяется парой фигурных скобок.

`break`

Прерывает текущую инструкцию цикла, ветвления или инструкцию с меткой и передаёт управление на инструкцию, следующую за прерываемой.

`continue`

Прерывает выполнение инструкции в текущей итерации текущего цикла или цикла с меткой и продолжает выполнение цикла со следующей итерации.

Пустая инструкция

Пустая инструкция используется тогда, когда никакой инструкции быть не должно, а по синтаксису JavaScript она требуется.

`if...else`

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

`switch`

Вычисляет выражение, сопоставляет вычисленное значение с выражением выбора и выполняет инструкции, ассоциированные с этим выбором.

`throw`

Выбрасывает пользовательское исключение.

`try...catch`

Помечает блок инструкций и определяет реакцию на возникновение исключения внутри помеченного блока инструкций.

### Объявления

`var`

Объявляет переменную, необязательно инициализирует её значением.

Экспериментальная возможность `let`

Объявляет локальную переменную в области видимости блока, необязательно инициализирует её значением.

Экспериментальная возможность `const`

Объявляет именованную константу только для чтения.

### Функции

`function`

Объявляет функцию с указанными параметрами.

Экспериментальная возможность `function*`

Функции-генераторы, упрощающие написание итераторов.

`return`

Определяет значение, возвращаемое из функции.

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

Смотрите статьи Что нового в JavaScript 1.7 и Итераторы и генераторы (en-US).

### Итерации

`do...while`

Создаёт цикл, выполняющий указанную инструкцию до тех пор, пока проверяющее условие равно `false`. Условие вычисляется после выполнения инструкции, так что указанная инструкция выполнится хотя бы один раз.

`for`

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

Устарело
Non-standard
 `for each...in`

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

`for...in`

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

Экспериментальная возможность `for...of`

Проходит по итерируемым объектам (включающим в себя массивы, массивоподобные объекты и итераторы с генераторами (en-US)), выполняя пользовательский хук с инструкциями, исполняющимися для значения каждого уникального свойства.

`while`

Создаёт цикл, выполняющий указанную инструкцию до тех пор, пока проверяющее условие равно `true`. Условие вычисляется перед выполнением инструкции..

### Прочее

`debugger`

Вызывает любую доступную функциональность отладки. Если функциональность отладки не доступна, эта инструкция ничего не делает.

`export`

Используется для экспорта подписанным скриптом свойств, функций и объектов в другие подписанные или неподписанные скрипты. Эта древняя функциональность Netscape была удалена и будет пересмотрена модулями ECMAScript 6.

`import`

Используется для импорта скриптом свойств, функций и объектов из подписанного скрипта, который предоставил эту информацию. Эта древняя функциональность Netscape была удалена и будет пересмотрена модулями ECMAScript 6.

`label`

Предоставляет инструкцию с идентификатором, на который вы можете сослаться с помощью инструкций `break` или `continue`.

Устарело `with`

Расширяет цепочку областей видимости инструкции.

Спецификации
------------

| Specification |
| --- |
| ECMAScript Language Specification # sec-async-function-definitions |
| ECMAScript Language Specification # sec-async-generator-function-definitions |
| ECMAScript Language Specification # sec-block |
| ECMAScript Language Specification # sec-break-statement |
| ECMAScript Language Specification # sec-class-definitions |
| ECMAScript Language Specification # sec-let-and-const-declarations |
| ECMAScript Language Specification # sec-continue-statement |
| ECMAScript Language Specification # sec-debugger-statement |
| ECMAScript Language Specification # sec-do-while-statement |
| ECMAScript Language Specification # sec-empty-statement |
| ECMAScript Language Specification # sec-exports |
| ECMAScript Language Specification # sec-for-statement |
| ECMAScript Language Specification # sec-for-in-and-for-of-statements |
| ECMAScript Language Specification # sec-function-definitions |
| ECMAScript Language Specification # sec-generator-function-definitions |
| ECMAScript Language Specification # sec-if-statement |
| ECMAScript Language Specification # sec-imports |
| ECMAScript Language Specification # sec-labelled-statements |
| ECMAScript Language Specification # sec-return-statement |
| ECMAScript Language Specification # sec-switch-statement |
| ECMAScript Language Specification # sec-throw-statement |
| ECMAScript Language Specification # sec-try-statement |
| ECMAScript Language Specification # sec-variable-statement |
| ECMAScript Language Specification # sec-while-statement |
| ECMAScript Language Specification # sec-with-statement |

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

* Операторы