return - JavaScript

return
======

Оператор **`return`** завершает выполнение текущей функции и возвращает её значение.

Интерактивный пример
--------------------

Синтаксис
---------
```

return [[выражение]];
```

`выражение`

Выражение, значение которого будет возвращено. Если не указано, вместо него возвращается `undefined`.

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

При вызове оператора `return` в функции её выполнение прекращается. Указанное значение возвращается в место вызова функции. Например, приведённая ниже функция возвращает возведённое в квадрат значение своего аргумента, `x` (где `x` – это число):

```js
function square(x) {
  return x \* x;
}
var demo = square(3);
// значение demo будет равняться 9
```

Если возвращаемое значение не указано, вместо него возвращается `undefined`.

Следующие выражения всегда прерывают выполнение функции:

```js
return;
return true;
return false;
return x;
return x + y / 3;
```

### Автоматическая расстановка точек с запятыми

На выражение `return` влияет автоматическая расстановка точек с запятыми (ASI). Разрыв строки не допускается между ключевым словом `return` и выражением.

```js
return
a + b;
```

трансформируется ASI в:

```js
return;
a + b;
```

В консоли появится предупреждение "unreachable code after return statement".

**Примечание:** Начиная с Gecko 40, предупреждение в консоли появляется, если обнаружен недостижимый код после `return`.

Для того, чтобы избежать данной проблемы (предотвратить ASI), можно использовать скобки:

```js
return (
  a + b;
);
```

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

### Прерывание функции

Функция немедленно останавливается в точке, где вызывается `return`.

```js
function counter() {
  for (var count = 1; ; count++) {
    // бесконечный цикл
    console.log(count + "A"); // до 5
    if (count === 5) {
      return;
    }
    console.log(count + "B"); // до 4
  }
  console.log(count + "C"); // никогда не появляется
}

counter();

// Выводит:
// 1A
// 1B
// 2A
// 2B
// 3A
// 3B
// 4A
// 4B
// 5A
```

### Возвращение функции

Смотрите также статью о замыканиях.

```js
function magic(x) {
  return function calc(x) {
    return x \* 42;
  };
}

var answer = magic();
answer(1337); // 56154
```

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

| Specification |
| --- |
| ECMAScript Language Specification # sec-return-statement |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

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

* Функции
* Замыкания


switch - JavaScript

switch
======

Сводка
------

**Инструкция switch** сравнивает выражение со случаями, перечисленными внутри неё, а затем выполняет соответствующие инструкции.

Синтаксис
---------
```

switch (expression) {
  case value1:
    //Здесь выполняются инструкции, если результат выражения равен value1
    [break;]
  case value2:
    //Инструкции, соответствующие value2
    [break;]
  ...
  case valueN:
    //Инструкции, соответствующие значению valueN
    //statementsN
    [break;]
  default:
    //Здесь находятся инструкции, которые выполняются при отсутствии соответствующего значения
    //statements_def
    [break;]
}
```

`expression`

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

`case valueN`

Случай, который проверяется на соответствие выражению (`expression`).

`statementsN`

Инструкции, которые выполняются, если `expression` соответствуют случаю.

`statements_def`

Инструкции, выполняемые если `expression` не соответствует ни одному случаю.

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

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

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

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

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

### Пример: Использование `switch`

В этом примере, если `expr` равно "Bananas", программа находит случай "Bananas" и выполняет соответствующие инструкции. При выполнении инструкции `break`, выполнение продолжится за пределами `switch`. Если бы `break` не было, то выполнились бы инструкции случая "Cherries".

```js
switch (expr) {
  case "Oranges":
    console.log("Oranges are $0.59 a pound.");
    break;
  case "Apples":
    console.log("Apples are $0.32 a pound.");
    break;
  case "Bananas":
    console.log("Bananas are $0.48 a pound.");
    break;
  case "Cherries":
    console.log("Cherries are $3.00 a pound.");
    break;
  case "Mangoes":
  case "Papayas":
    console.log("Mangoes and papayas are $2.79 a pound.");
    break;
  default:
    console.log("Sorry, we are out of " + expr + ".");
}

console.log("Is there anything else you'd like?");
```

### Пример: Что случится, если не использовать break?

Если вы не использовали инструкцию `break`, то будут выполнены инструкции следующего случая. И проверка на соответствие выражению не будет выполняться.

```js
var foo = 0;
switch (foo) {
  case -1:
    console.log("negative 1");
    break;
  case 0: // foo равно 0, случай соответствует выражению и эти инструкции будут выполнены
    console.log(0);
  // ПРИМЕЧАНИЕ: здесь могла находиться забытая инструкция break
  case 1: // В случае 'case 0:' не было break, инструкции данного случая также будут выполнены
    console.log(1);
    break; // В конце расположен break, поэтому выполнение не перейдёт к случаю 'case 2:'
  case 2:
    console.log(2);
    break;
  default:
    console.log("default");
}
```

### Цепочки case

#### Одна операция

 Этот метод использует тот факт, что после `case` нет прерывания и продолжится
 выполнение следующего `case` независимо от того, соответствует ли `case`
 предоставленному условию. Подробнее в примере "Что случится, если не использовать break?.

Это пример `case` с одной операцией, где четыре разных значения отрабатывают одинаково.

```js
var Animal = "Giraffe";
switch (Animal) {
  case "Cow":
  case "Giraffe":
  case "Dog":
  case "Pig":
    console.log("This animal is not extinct.");
    break;
  case "Dinosaur":
  default:
    console.log("This animal is extinct.");
}
```

#### Цепочка операций

 Это пример множественных операций внутри `case`, где в зависимости от
 предоставленного числа можно увидеть разный вывод. Здесь показывается, что операции
 отрабатывают в том порядке, в котором расположены `case`. При этом
 числовая последовательность может не соблюдаться. Также возможно примешать в
 `case` строки.

```js
var foo = 1;
var output = "Output: ";
switch (foo) {
  case 0:
    output += "So ";
  case 1:
    output += "What ";
    output += "Is ";
  case 2:
    output += "Your ";
  case 3:
    output += "Name";
  case 4:
    output += "?";
    console.log(output);
    break;
  case 5:
    output += "!";
    console.log(output);
    break;
  default:
    console.log("Please pick a number from 0 to 5!");
}
```

Вывод этого примера:

| Значение | Лог |
| --- | --- |
| `foo` is `NaN` or not `1`, `2`, `3`, `4`, `5`, or `0` | Please pick a number from 0 to 5! |
| `0` | Output: So What Is Your Name? |
| `1` | Output: What Is Your Name? |
| `2` | Output: Your Name? |
| `3` | Output: Name? |
| `4` | Output: ? |
| `5` | Output: ! |

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

| Specification |
| --- |
| ECMAScript Language Specification # sec-switch-statement |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

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

* `if...else`


function - JavaScript

function
========

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

Также вы можете определять функции через конструктор `Function` или `функциональное выражение`.

Синтаксис
---------
```

function name([param,[, param,[..., param]]]) {
   [statements]
}
```

`name`

Имя функции.

`paramN`

Имя аргумента, переданного в функцию. Максимальное количество аргументов, которое может принять функция, может варьироваться в зависимости от движков.

`statements`

Инструкции, которые составляют тело функции.

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

Функция, созданная через объявление функции, является объектом `Function` и обладает всеми свойствами, методами и поведением объектов такого типа. Смотрите `Function` для подробной информации о функциях.

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

По умолчанию функции возвращают `undefined`. Чтобы вернуть другое значение, функция должна содержать инструкцию `return`, которая указывает, какое значение возвращать.

### Функции, создаваемые по условию

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

### Поднимание объявленной функции

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

```js
hoisted(); // выводит "foo"

function hoisted() {
  console.log("foo");
}
```

Заметьте, что `функциональные выражения` не поднимаются:

```js
notHoisted(); // TypeError: notHoisted не является функцией

var notHoisted = function () {
  console.log("bar");
};
```

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

### Использование `function`

Следующий код объявляет функцию, возвращающую итоговое количество продаж, сложенное из количества продаж продуктов `a`, `b` и `c`.

```js
function calc\_sales(units\_a, units\_b, units\_c) {
  return units_a \* 79 + units_b \* 129 + units_c \* 699;
}
```

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

| Specification |
| --- |
| ECMAScript Language Specification # sec-function-definitions |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

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

* `Функции и область видимости` (en-US)
* `Function`
* `Функциональное выражение`
* `Оператор function*`
* `Выражение function*`
* `Стрелочные функции`
* `GeneratorFunction`


async function - JavaScript

async function
==============

Baseline Widely available
-------------------------

This feature is well established and works across many devices and browser versions. It’s been available across browsers since April 2017.

* Learn more
* See full compatibility
* Report feedback
Объявление **`async function`** определяет *асинхронную функцию*, которая возвращает объект `AsyncFunction`.

Вы также можете определить async-функции, используя `выражение async function`.

Синтаксис
---------
```

async function name([param[, param[, ... param]]]) {
   statements
}
```

`name`

Имя функции.

`param`

Имя аргумента, который будет передан в функцию.

`statements`

Выражение, содержащее тело функции.

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

После вызова функция `async` возвращает `Promise`. Когда результат был получен, `Promise` завершается, возвращая полученное значение. Когда функция `async` выбрасывает исключение, `Promise` ответит отказом с выброшенным (`throws`) значением.

Функция async может содержать выражение `await`, которое приостанавливает выполнение функции async и ожидает ответа от переданного `Promise`, затем возобновляя выполнение функции `async` и возвращая полученное значение.

Ключевое слово `await` допустимо только в асинхронных функциях. В другом контексте вы получите ошибку `SyntaxError`.

**Примечание:** Цель функций async/await упростить использование promises синхронно и воспроизвести некоторое действие над группой `Promises`. Точно так же как `Promises` подобны структурированным колбэкам, async/await подобна комбинации генераторов и promises.

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

### Простой пример

```js
function resolveAfter2Seconds(x) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function add1(x) {
  const a = await resolveAfter2Seconds(20);
  const b = await resolveAfter2Seconds(30);
  return x + a + b;
}

add1(10).then((v) => {
  console.log(v); // prints 60 after 4 seconds.
});

async function add2(x) {
  const a = resolveAfter2Seconds(20);
  const b = resolveAfter2Seconds(30);
  return x + (await a) + (await b);
}

add2(10).then((v) => {
  console.log(v); // prints 60 after 2 seconds.
});
```

**Предупреждение:** #### Не путайте await и Promise.allФункция `add1` приостанавливается на 2 секунды для первого `await` и ещё на 2 для второго. Второй таймер создаётся только после срабатывания первого. В функции `add2` создаются оба и оба же переходят в состояние `await`. В результате функция `add2` завершится скорее через две, чем через четыре секунды, поскольку таймеры работают одновременно. Однако запускаются они всё же не параллельно, а друг за другом - такая конструкция не означает автоматического использования `Promise.all`. Если два или более Promise должны разрешаться параллельно, следует использовать `Promise.all`.

### Когда функция `async` выбрасывает исключение

```js
async function throwsValue() {
  throw new Error("oops");
}
throwsValue().then(
  (resolve) => {
    console.log("resolve:" + resolve);
  },
  (reject) => {
    console.log("reject:" + reject);
  },
);
//prints "reject:Error: oops"
//or
throwsValue()
  .then((resolve) => {
    console.log("resolve:" + resolve);
  })
  .catch((reject) => {
    console.log("reject:" + reject);
  });
//prints "reject:Error: oops"
```

### Перепись цепочки promise с использованием функции `async`

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

```js
function getProcessedData(url) {
  return downloadData(url) // returns a promise
    .catch((e) => {
      return downloadFallbackData(url); // returns a promise
    })
    .then((v) => {
      return processDataInWorker(v); // returns a promise
    });
}
```

он может быть переписан с одним использованием функции `async`:

```js
async function getProcessedData(url) {
  let v;
  try {
    v = await downloadData(url);
  } catch (e) {
    v = await downloadFallbackData(url);
  }
  return processDataInWorker(v);
}
```

Заметьте, что пример выше не содержит `await` на `return`, потому что возвращаемое значение функции `async` неявно обёрнуто в `Promise.resolve`.

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

| Specification |
| --- |
| ECMAScript Language Specification # sec-async-function-definitions |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

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

* `async function expression`
* `AsyncFunction` object
* `await`


try...catch - JavaScript

try...catch
===========

Конструкция `try...catch` пытается выполнить инструкции в блоке `try`, и, в случае ошибки, выполняет блок `catch`.

Синтаксис
---------
```

try {
   try_statements
}
[catch (exception_var_1 if condition_1) { // не стандартно
   catch_statements_1
}]
...
[catch (exception_var_2) {
   catch_statements_2
}]
[finally {
   finally_statements
}]
```

`try_statements`

Инструкции для выполнения.

`catch_statements_1`, `catch_statements_2`

Инструкции, которые будут выполнены, если произойдёт ошибка в блоке `try`.

`exception_var_1`, `exception_var_2`

Идентификатор для хранения объекта ошибки, который впоследствии используется в блоке `catch`

`condition_1`

Условное выражение.

`finally_statements`

Инструкции, которые выполняются после завершения блока `try`. Выполнение происходит в независимости от того, произошла ошибка или нет.

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

Конструкция `try` содержит блок `try`, в котором находится одна или несколько инструкций (Блок (`{}` ) обязательно должен присутствовать, даже если выполняется всего одна инструкция), и хотя бы один блок `catch` или `finally`. Таким образом, есть три основные формы конструкции `try`:

1. `try {...} catch {...}`
2. `try {...} finally {...}`
3. `try {...} catch {...} finally {...}`

Блок `catch` содержит инструкции, которые будут выполнены, если в блоке `try` произошла ошибка. Если любая инструкция в блоке `try` выбрасывает исключение, то управление сразу же переходит в блок `catch`. Если в блок `try` не было выброшено исключение, то блок `catch` не выполняется.

Блок `finally` выполнится после выполнения блоков `try` и `catch`, но перед инструкциями, следующими за конструкцией `try...catch`. Он выполняется всегда, в независимости от того, было исключение или нет.

Вы можете использовать вложенные конструкции `try`. Если внутренняя конструкция `try` не имеет блока `catch` (такое может быть при её использовании в виде `try {...} finaly {...}`, потому что `try {...}` не может быть без блоков catch или `finally`), будет вызван `сatch` внешней конструкции `try`.

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

### Безусловный блок catch

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

```js
try {
  throw "myException"; // создание исключения
} catch (e) {
  // инструкции для обработки ошибок
  logMyErrors(e); // передать объект исключения обработчику ошибок
}
```

Блок `catch` задаёт идентификатор (`e` в примере выше) который содержит объект исключения (в примере выше — значение, переданное оператору `throw`). Область видимости этого объекта ограничивается блоком `catch`.

### Условный блок `catch`

"Условные блоки `catch`" можно создавать, используя `try...catch` с `if...else if...else`, как здесь:

```js
try {
  myroutine(); // может выбрасывать три вида исключений
} catch (e) {
  if (e instanceof TypeError) {
    // обработка исключения TypeError
  } else if (e instanceof RangeError) {
    // обработка исключения RangeError
  } else if (e instanceof EvalError) {
    // обработка исключения EvalError
  } else {
    // обработка остальных исключений
    logMyErrors(e); // передать обработчику ошибок
  }
}
```

Частый сценарий использования — обработать известные исключения, а при неизвестных ошибках, пробросить их дальше:

```js
try {
  myRoutine();
} catch (e) {
  if (e instanceof RangeError) {
    // обработка известного исключения, с которым
    // понятно, что делать
  } else {
    throw e; // пробросить неизвестные ошибки
  }
}
```

**Примечание:** Firefox раньше поддерживал краткую запись условных блоков `catch`:
```

try {
  myroutine(); // может выбрасывать три вида исключения
} catch (e if e instanceof TypeError) {
  // обработка исключений TypeError
} catch (e if e instanceof RangeError) {
  // обработка исключений RangeError
} catch (e if e instanceof EvalError) {
  // обработка исключений EvalError
} catch (e) {
  // обработка остальных исключения
  logMyErrors(e);
}
```

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

### Идентификатор исключения

Когда в блоке `try` выбрасывается исключение, `exception_var` (т. е. `e` в конструкции `catch (e)`) содержит значение исключения. Его можно использовать, чтобы получить больше информации об выброшенном исключении. Идентификатор доступен только в области видимости блока `catch`.

```js
try {
  if (!firstValidation()) {
    throw 1;
  }
  if (!secondValidation()) {
    throw 2;
  }
} catch (e) {
  // Выводит 1 или 2 (если не произошло никаких других ошибок)
  console.log(e);
}
```

### Блок finally

Блок `finally` содержит код который будет запущен после кода в блоках `try` и `catch`. Обратите внимание, что код в блоке `finally` запускается в независимости от того, было ли выброшено исключение или нет. Также код в блоке `finally` будет запущен вне зависимости от того, присутствует блок `catch` или нет. Блок `finally` можно использовать для того, чтобы скрипт безопасно завершил работу в случае ошибки. Например, если необходимо освободить память и ресурсы которые использовал скрипт.

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

```js
function expensiveCalculations() {
  // Сложные вычисления
}

function maybeThrowError() {
  // Функция, которая может выбросить исключение
  if (Math.random() > 0.5) throw new Error();
}

try {
  // Теперь при прокрутке страницы будут происходить
  // сложные вычисления, что сильно скажется на
  // производительности
  window.addEventListener("scroll", expensiveCalculations);
  maybeThrowError();
} catch {
  // Если функция maybeThrowError выбросит исключения,
  // управление сразу перейдёт в блок catch и
  // сложные вычисления продолжат выполняться до
  // перезагрузки страницы
  maybeThrowError();
}
window.removeEventListener("scroll", expensiveCalculations);
```

В этом примере, если функция `maybeThrowError` выбросит исключение внутри блока `try`, управление перейдёт в блок `catch`. Если и в блоке `catch` эта функция тоже выбросит исключение, то выполнение кода прервётся, и обработчик события не будет снят, пока пользователь не перезагрузит страницу, что плохо скажется на скорости работы. Для того, чтобы избежать таких ситуаций, следует использовать блок `finally`:

```js
try {
  window.addEventListener("scroll", expensiveCalculations);
  maybeThrowError();
} catch {
  maybeThrowError();
} finally {
  window.removeEventListener("scroll", expensiveCalculations);
}
```

Другой пример: работа с файлами. В следующем фрагменте кода показывается, как скрипт открывает файл и записывает в него какие-то данные (в серверном окружении JavaScript имеет доступ к файловой системе). Во время записи может произойти ошибка. Но после открытия файл очень важно закрыть, потому что незакрытый файл может привести к утечкам памяти. В таких случаях используется блок `finally`:

```js
openMyFile();
try {
  // Сделать что-то с файлом
  writeMyFile(theData);
} finally {
  closeMyFile(); // Закрыть файл, что бы ни произошло
}
```

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

### Вложенные блоки try

Для начала давайте посмотрим что делает этот код:

```js
try {
  try {
    throw new Error("упс");
  } finally {
    console.log("finally");
  }
} catch (e) {
  console.error("внешний блок catch", e.message);
}

// Вывод:
// "finally"
// "внешний блок catch" "упс"
```

Теперь отловим исключение во внутреннем блоке `try`, добавив к нему блок `catch`:

```js
try {
  try {
    throw new Error("упс");
  } catch (e) {
    console.error("внутренний блок catch", e.message);
  } finally {
    console.log("finally");
  }
} catch (e) {
  console.error("внешний блок catch", e.message);
}

// Output:
// "внутренний блок catch" "упс"
// "finally"
```

Наконец, пробросим ошибку

```js
try {
  try {
    throw new Error("упс");
  } catch (e) {
    console.error("внутренний блок catch", e.message);
    throw e;
  } finally {
    console.log("finally");
  }
} catch (e) {
  console.error("внешний блок catch", e.message);
}

// Вывод:
// "внутренний блок catch" "oops"
// "finally"
// "внешний блок catch" "oops"
```

Любое исключение будет передано только в ближайший блок `catch`, если он не пробросит его дальше. Все исключения, выброшенными внутренними блоками (потому что код в блоке `catch` также может выбросить исключение), будут пойманы внешними.

### Возвращение значения из блока finally

Если блок `finally` возвращает какое-либо значение, оно становится значением, которое возвращает вся конструкция `try...catch...finally`, вне зависимости от любых инструкций `return` в блоках `try` и `catch`. Также игнорируются исключения, выброшенные блоком `catch`.

```js
(() => {
  try {
    try {
      throw new Error("oops");
    } catch (ex) {
      console.error("inner", ex.message);
      throw ex;
    } finally {
      console.log("finally");
      return;
    }
  } catch (ex) {
    console.error("outer", ex.message);
  }
})();

// Logs:
// "inner" "oops"
// "finally"
```

"упс" не доходит до внешнего блока из-за инструкции `return` в блоке `finally`. То же самое произойдёт с любым значением, возвращаемым из блока `catch`.

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

| Specification |
| --- |
| ECMAScript Language Specification # sec-try-statement |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

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

* `Error`
* `throw`


const - JavaScript

const
=====

Значение констант не может быть изменено новым присваиванием, а также не может быть переопределено. Константы (`const`) подчиняются области видимости уровня блока так же, как переменные, объявленные с использованием ключевого слова `let`.

Синтаксис
---------
```

const name1 = value1 [, name2 = value2 [, ... [, nameN = valueN]]];
```

`nameN`

Имя константы. Подчиняется тем же правилам, что и идентификаторы обычных переменных.

`valueN`

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

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

Это объявление создаёт константу, чья область действия может быть как глобальной, так и локальной внутри блока, в котором она объявлена. Глобальные константы не становятся свойствами объекта window, в отличие от `var`-переменных. Инициализация константы обязательна; необходимо указать значение одновременно с объявлением (смысл в том, что потом это значение изменить уже нельзя).

**`const`** создаёт константу (новую именованную ссылку на область памяти), доступную только для чтения. Это не означает, что указываемое значение неизменно, но это означает, что идентификатор не может быть переназначен. Например, если константа указывает на объект, то сам объект может быть изменён.

Все соображения по поводу временных мёртвых зон, применимы и к `let` и к `const`.

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

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

Ниже описаны примеры, демонстрирующие поведение **`const`.** Попробуйте их выполнить в простом JavaScript-редакторе или в консоли браузера.

```js
// Примечание: Идентификаторы констант могут быть объявлены как в верхнем,
// так и в нижнем регистре. Но правилом хорошего тона является использование
// верхнего регистра.

// определим MY\_FAV как константу и присвоим ей значение 7
const MY\_FAV = 7;

// данное присваивание выдаст ошибку - Uncaught TypeError: Assignment to constant variable.
MY\_FAV = 20;

// напечатает 7
console.log("my favorite number is: " + MY\_FAV);

// попробуем переопределить константу, будет брошено исключение - Uncaught SyntaxError: Identifier 'MY\_FAV' has already been declared
const MY\_FAV = 20;

// имя MY\_FAV зарезервировано константой выше, данная операция
// выкинет исключение
var MY\_FAV = 20;

// здесь также будет ошибка
let MY\_FAV = 20;

// Важно отметить свойства области видимости уровня блока
if (MY\_FAV === 7) {
    // Всё нормально. Создать константу или переменную MY\_FAV можно.
    // (работает так же как и let при объявлении в блоке переменных, которые не const)
    const MY\_FAV = 20;

    // MY\_FAV теперь 20
    console.log("my favorite number is " + MY\_FAV);

    // это попадёт в глобальный контекст и выдаст ошибку
    var MY\_FAV = 20;
}

// MY\_FAV все ещё имеет значение 7
console.log("my favorite number is " + MY\_FAV);

// Выдаст ошибку, потому что константа не инициализирована - Uncaught SyntaxError: Missing initializer in const declaration
const FOO;

// const также работает с объектами
const MY\_OBJECT = {"key": "value"};

// Попытка переопределить ссылку на объект вызовет исключение - Uncaught TypeError: Assignment to constant variable.
MY\_OBJECT = {"OTHER\_KEY": "value"};

// но, свойства объекта (ключи) можно изменять
MY\_OBJECT.key = "otherValue"; // Используйте Object.freeze() для того, чтобы сделать объект неизменяемым

// То же самое применимо к массивам
const MY\_ARRAY = [];
// Например, можно добавлять элементы в массив
MY\_ARRAY.push("A"); // ["A"]
// Но менять ссылку на объект массива нельзя. Это вызовет исключение - Uncaught TypeError: Assignment to constant variable
MY\_ARRAY = ["B"]
```

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

| Specification |
| --- |
| ECMAScript Language Specification # sec-let-and-const-declarations |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

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

* `var`
* `let`
* Константы в справочнике JavaScript


for...in - JavaScript

for...in
========

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

Синтаксис
---------
```

for (variable in object) {...
}
```

`variable`

Другое (очередное) имя свойства назначается переменной на каждой итерации.

`object`

Объект, по чьим свойствам мы проходим

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

Цикл `for...in` проходит только по перечисляемым свойствам. Объекты, созданные встроенными конструкторами, такими как `Array` и `Object` имеют неперечисляемые свойства от `Object.prototype` и `String.prototype`, например, от `String`-это `indexOf()`, а от `Object` - метод `toString()`. Цикл пройдёт по всем перечисляемым свойствам объекта, а также тем, что он унаследует от конструктора прототипа (свойства объекта в цепи прототипа).

### Удаление, добавление и модификация свойств

Цикл `for...in` проходит по свойствам в произвольном порядке (см. оператор `delete` для того, чтобы узнать почему порядок прохода может отличаться в зависимости от браузера). Если свойство изменяется за одну итерацию, а затем изменяется снова, его значением в цикле является его последнее значение. Свойство, удалённое до того, как до него дошёл цикл, не будет участвовать в нём. Свойства добавленные в объекты в цикле могут быть пропущены. В общем, лучше не добавлять, изменять или удалять свойство из объекта во время итерации, если по нему ещё не прошли. Нет гарантии, что добавленное свойство будет посещено циклом, низменное после проведения изменений, а удалённое после удаления.

### `Проход по массиву и for...in`

**Примечание:** `for...in` не следует использовать для `Array`, где важен порядок индексов.

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

Так как порядок прохода зависит от реализации, проход по массиву может не произойти в правильном порядке. Следовательно лучше с числовыми индексами использовать циклы `for`, `Array.prototype.forEach()` или `for...of`, когда проходим по массивам, где важен порядок доступа к свойствам.

### Проход только через собственные свойства

Если вы хотите рассматривать только свойства самого объекта, а не его прототипов, используйте `getOwnPropertyNames()`, `hasOwnProperty()` или `propertyIsEnumerable`. Кроме того, если вы знаете, что не будет вмешательства в код извне, вы можете расширить встроенные прототипы методом проверки.

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

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

```js
var obj = { a: 1, b: 2, c: 3 };

for (var prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}

// Выведет:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"
```

Следующая функция иллюстрирует использование `hasOwnProperty()`: наследуемые свойства не отображаются

```js
var triangle = { a: 1, b: 2, c: 3 };

function ColoredTriangle() {
  this.color = "red";
}

ColoredTriangle.prototype = triangle;

var obj = new ColoredTriangle();

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    console.log("obj." + prop + " = " + obj[prop]);
  }
}

// Выведет:
// "obj.color = red"
```

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

| Specification |
| --- |
| ECMAScript Language Specification # sec-for-in-and-for-of-statements |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Поддержка: инициализатор переменных
-----------------------------------

До SpiderMonkey 40, можно было инициализировать переменные (`i=0`) в цикле `for...in`:

```js
var obj = {a:1, b:2, c:3};
for(var i=0 in obj) {
  console.log(obj[i]);
}
// 1
// 2
// 3
```

Это нестандартное поведение игнорируется в версии 40 и более поздних, оно бросит предупреждение `SyntaxError` ("for-in loop head declarations may not have initializers") в консоль (Firefox bug 748550 и Firefox bug 1164741).

Другие движки, такие как v8 (Chrome), Chakra (IE/Edge) и JSC (WebKit/Safari) также собираются удалить это нестандартное поведение.

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

* `for...of` - похожий цикл, проходящий по значениям свойств
* `for each in` - похожее выражение, но перебирает значения свойств, а не их имена (устарело)
* `for`
* Генераторы (использующие синтаксис `for...in`)
* Перечислимость и владение свойствами
* `Object.getOwnPropertyNames()`
* `Object.prototype.hasOwnProperty()`
* `Array.prototype.forEach()`


Модули JavaScript - JavaScript

Модули JavaScript
=================

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

Это руководство содержит всю необходимую информацию для начала работы с модулями JavaScript.

Модули: история вопроса
-----------------------

Сначала программы на JavaScript были небольшими — в прежние времена они использовались для изолированных задач, добавляя при необходимости немного интерактивности веб-страницам, так что большие скрипты в основном не требовались. Прошло несколько лет, и вот мы уже видим полномасштабные приложения, работающие в браузерах и содержащие массу кода на JavaScript; кроме того, язык стал использоваться и в других контекстах (например, Node.js).

Таким образом, в последние годы появились причины на то, чтобы подумать о механизмах деления программ на JavaScript на отдельные модули, которые можно импортировать по мере необходимости. Node.js включал такую возможность уже давно, кроме того, некоторые библиотеки и фреймворки JavaScript разрешали использование модулей (например, CommonJS и основанные на AMD системы модулей типа RequireJS, а позднее также Webpack и Babel).

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

Встроенная обработка модулей JavaScript связана с инструкциями `import` и `export`, их поддержка браузерами показана в следующих таблицах.

Совместимость с браузерами
--------------------------

### javascript.statements.import

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

### javascript.statements.export

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Пример использования модулей
----------------------------

 Для того, чтобы продемонстрировать использование модулей, мы создали простой набор примеров, который вы можете найти на GitHub.
 В этих примерах мы создаём элемент `<canvas>`
 на веб-странице и затем рисуем различные фигуры на нём (и выводим информацию об этом).

Примеры довольно тривиальны, но они намеренно сделаны простыми для ясной демонстрации модулей.

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

Базовая структура примера
-------------------------

В первом примере (см. директорию basic-modules) у нас следующая структура файлов:
```

index.html
main.js
modules/
    canvas.js
    square.js
```

**Примечание:** Все примеры в этом руководстве в основном имеют одинаковую структуру.

Давайте разберём два модуля из директории modules:

* `canvas.js` — содержит функции, связанные с настройкой canvas:
    + `create()` — создаёт холст заданной ширины `width` и высоты `height` внутри `<div>`-обертки с указанным `id` и помещённой в родителя `parent`. Результатом выполнения функции будет объект, содержащий 2D-контекст холста и id обертки.
    + `createReportList()` — создаёт неупорядоченный список, добавленный внутри указанного элемента-обёртки, который можно использовать для вывода данных отчёта. Возвращает id списка.
* `square.js` — содержит:
    + `name` — переменная со строковым значением 'square'.
    + `draw()` — функция, рисующая квадрат на указанном холсте с заданными размером, положением и цветом. Возвращает объект, содержащий размер, положение и цвет квадрата.
    + `reportArea()` — функция, которая выводит посчитанную площадь квадрата в указанный список отчета.
    + `reportPerimeter()` — функция, которая выводи посчитанный периметр квадрата в указанный список отчета.
Взгляд со стороны — `.mjs` против `.js`
---------------------------------------

В этой статье мы используем расширение `.js` для файлов наших модулей, но в других источниках вы можете встретить расширение `.mjs`. Например, в документации движка V8 используется `.mjs`. Причины следующие:

* Это полезно для ясности, то есть дает понять, какие файлы являются модулями, а какие — обычными JavaScript-файлами.
* Это гарантирует, что файлы вашего модуля будут проанализированы как модуль средами выполнения, такими как Node.js, и инструментами сборки, такими как Babel.

Тем не менее, мы решили продолжать использовать `.js`, по крайней мере на данным момент. Чтобы модули корректно работали в браузере, вам нужно убедиться, что ваш сервер отдаёт их с заголовком `Content-Type`, который содержит JavaScript MIME type такой как `text/javascript`. В противном случае вы получете ошибку проверки MIME type — "The server responded with a non-JavaScript MIME type", и браузер не сможет запустить ваш JavaScript. Большинство серверов уже имеют правильный тип для `.js`-файлов, но ещё не имеют нужного MIME type для `.mjs`-файлов. Серверы, которые уже отдают `.mjs` файлы корректно, включают в себя GitHub Pagesи `http-сервер` для Node.js.

 Это нормально, если вы уже используете такую среду или ещё нет, но знаете, что делать, и имеете нужные доступы (то есть вы можете настроить свой сервер, чтобы он устанавливал корректный `Content-Type`-заголовок для `.mjs`-файлов).
 Однако это может вызвать путаницу, если вы не контролируете сервер, с которого отдаются файлы, или публикуете файлы для общего пользования, как мы здесь.

В целях обучения и переносимости на разные платформы мы решили остановится на `.js`.

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

Также стоит отметить, что:

* Некоторые инструменты могут никогда не добавить поддержку `.mjs`, например, TypeScript.
* `<script type="module">` атрибут используется для обозначения того, что файл является модулем. Вы увидите примеры использования данного атрибута ниже.
Экспорт функциональности модуля
-------------------------------

Первое, что нужно сделать, чтобы получить доступ к функциональности модуля, — экспортировать его. Это делается с помощью инструкции `export`.

Самый простой способ использовать экспорт — поместить конструкцию `export` перед любыми элементами, которые вы хотите экспортировать из модуля, например:

```js
export const name = "square";

export function draw(ctx, length, x, y, color) {
  ctx.fillStyle = color;
  ctx.fillRect(x, y, length, length);

  return {
    length: length,
    x: x,
    y: y,
    color: color,
  };
}
```

 Вы можете экспортировать `var`-, `let`-, `const`-переменные, и — как мы увидим позже — классы.
 Они должны быть в верхней области видимости, вы не можете использовать `export` внутри функции, например.

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

```js
export { name, draw, reportArea, reportPerimeter };
```

Импорт функциональности в ваш скрипт
------------------------------------

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

```js
import { name, draw, reportArea, reportPerimeter } from "./modules/square.js";
```

Используйте конструкцию `import`, за которой следует разделенный запятыми список функций, которые вы хотите импортировать, заключённый в фигурные скобки, за которым следует ключевое слово from, за которым следует путь к файлу модуля — путь относительно корня сайта, который для нашего примера `basic-modules` будет равен `/js-examples/modules/basic-modules`.

 Однако, мы написали путь немного иначе — мы используем (`.`) синтаксис, означающий "текущую директорию", за которым следует путь к файлу, который мы пытаемся найти.
 Это намного лучше, чем каждый раз записывать весь относительный путь, поскольку он короче и делает URL-адрес переносимым - пример все равно будет работать, если вы переместите его в другое место в иерархии сайта.

Так например:
```

/js-examples/modules/basic-modules/modules/square.js
```

становится
```

./modules/square.js
```

Вы можете найти подобные строки кода в файле `main.js`.

**Примечание:** В некоторых модульных системах вы можете опустить расширение файла и начальные `/`, `./`, or `../` (например `'modules/square'`). Это не работает в нативных JavaScript-модулях.

 После того, как вы импортировали функции в свой скрипт, вы можете использовать их так же, как если бы они были определены в этом же файле.
 Следующий пример можно найти в `main.js`, сразу за строками импорта:

```js
let myCanvas = create("myCanvas", document.body, 480, 320);
let reportList = createReportList(myCanvas.id);

let square1 = draw(myCanvas.ctx, 50, 50, 100, "blue");
reportArea(square1.length, reportList);
reportPerimeter(square1.length, reportList);
```

**Примечание:** Хотя импортированные функции доступны в файле, они доступны только для чтения.
 Вы не можете изменить импортированную переменную, но вы всё равно можете изменять свойства у `const`-переменных.
 Кроме того, переменные импортируется как "live bindings" -
 это означает, что они могут меняться по значению, даже если вы не можете изменить привязку, в отличие от `const`.

Добавление модуля на HTML-страницу
----------------------------------

Далее нам необходимо подключить модуль `main.js` на нашу HTML-страницу. Это очень похоже на то, как мы подключаем обычный скрипт на страницу, с некоторыми заметными отличиями.

Прежде всего, вам нужно добавить `type="module"` в `<script>`-элемент, чтобы объявить, что скрипт является модулем. Чтобы подключить модуль `main.js`, нужно написать следующее:

```html
<script type="module" src="main.js"></script>
```

Вы также можете встроить скрипт модуля непосредственно в HTML-файл, поместив JavaScript-код внутрь `<script>`-элемента:

```js
<script type="module">/\* код JavaScript модуля \*/</script>
```

Скрипт, в который вы импортируете модуль, в основном действует как модуль верхнего уровня. Если вы упустите это, то Firefox, например, выдаст ошибку "SyntaxError: import declarations may only appear at top level of a module".

Вы можете использовать `import` и `export` инструкции только внутри модулей, внутри обычных скриптов они работать не будут.

Другие отличия модулей от обычных скриптов
------------------------------------------

* Вы должны быть осторожны во время локального тестирование — если вы попытаетесь загрузить файл HTML локально (то есть по `file://` URL), вы столкнётесь с ошибками CORS из-за требований безопасности JavaScript-модулей. Вам нужно проводить тестирование через сервер.
* Также обратите внимание, что вы можете столкнуться с отличным от обычных файлов поведением кода в модулях. Это происходит из-за того, что модули используют strict mode автоматически.
* Нет необходимости использовать атрибут `defer` (см. атрибуты `<script>` элемента) при загрузке модуля, модули являются deferred по умолчанию.
* Модули выполняются только один раз, даже если на них есть ссылки в нескольких `<script>` тэгах.
* И последнее, но не менее важное: функциональность модуля импортируются в область видимости одного скрипта, она недоступны в глобальной области видимости. Следовательно, вы сможете получить доступ к импортированным частям модуля только в скрипте, в который он импортирован, и, например, вы не сможете получить к нему доступ из консоли JavaScript. Вы по-прежнему будете получать синтаксические ошибки в DevTools, но вы не сможете использовать некоторые методы отладки, которые, возможно, ожидали использовать.
Экспорт по умолчанию против именованного экспорта
-------------------------------------------------

Экспорты функций и переменных, которые мы использовали в примерах выше являются **именованными экспортами** — каждый элемент (будь то функция или `const`-переменная, например) упоминается по имени при экспорте, и это имя также используется для ссылки на него при импорте.

Существует также тип экспорта, который называется **экспорт по умолчанию** — он существует для удобного экспорта основной функции, а также помогает модулям JavaScript взаимодействовать с существующими модульными системами CommonJS и AMD (это хорошо объясняется в статье Джейсона Орендорфа ES6 в деталях: Модули, ищите по ключевому слову «Default exports»).

Давайте посмотрим на пример и объясним, как это работает. В модуле `square.js` из нашего примера вы можете найти функцию `randomSquare()`, которая создаёт квадрат со случайным цветом, размером и координатами. Мы хотим экспортировать эту функции по умолчанию, поэтому в конце файла пишем следующее:

```js
export default randomSquare;
```

Обратите внимание на отсутствие фигурных скобок.

Кстати, можно было бы определить функцию как анонимную и добавить к ней `export default`:

```js
export default function(ctx) {
  ...
}
```

В нашем файле `main.js` мы импортируем функцию по умолчанию, используя эту строку:

```js
import randomSquare from "./modules/square.js";
```

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

```js
import { default as randomSquare } from "./modules/square.js";
```

**Примечание:** «as» синтаксис для переименования экспортируемых элементов поясняется ниже в разделе Переименование импорта и экспорта.

Как избежать конфликтов имён
----------------------------

 Пока что наши модули для рисования фигур на холсте работают нормально.
 Но что произойдёт, если мы попытаемся добавить модуль, который занимается рисованием другой фигуры, например круга или треугольника?
 С этими фигурами, вероятно, тоже будут связаны такие функции, как `draw()`, `reportArea()` и т.д.;
 если бы мы попытались импортировать разные функции с одним и тем же именем в один и тот же файл модуля верхнего уровня, мы бы столкнулись с конфликтами и ошибками.

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

Переименование импорта и экспорта
---------------------------------

Можно изменять имя функциональности в целевом модуле с помощью ключевого слова `as` внутри фигурных скобок инструкций `import` и `export`.

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

```js
// внутри module.js
export { function1 as newFunctionName, function2 as anotherNewFunctionName };

// внутри main.js
import { newFunctionName, anotherNewFunctionName } from "./modules/module.js";
```

```js
// внутри module.js
export { function1, function2 };

// внутри main.js
import {
  function1 as newFunctionName,
  function2 as anotherNewFunctionName,
} from "./modules/module.js";
```

 Давайте посмотрим на реальный пример. В нашей renaming директории
 вы увидите ту же модульную систему, что и в предыдущем примере,
 за исключением того, что мы добавили модули `circle.js` и `triangle.js` для рисования кругов и треугольников и создания отчетов по ним.

Внутри каждого из этих модулей у нас есть функции с одинаковыми именами, которые экспортируются, и поэтому у каждого из них есть один и тот же оператор `export` внизу файла:

```js
export { name, draw, reportArea, reportPerimeter };
```

Если бы в `main.js` при их импорте мы попытались использовать

```js
import { name, draw, reportArea, reportPerimeter } from "./modules/square.js";
import { name, draw, reportArea, reportPerimeter } from "./modules/circle.js";
import { name, draw, reportArea, reportPerimeter } from "./modules/triangle.js";
```

то браузер выдал бы ошибку — "SyntaxError: redeclaration of import name" (Firefox).

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

```js
import {
  name as squareName,
  draw as drawSquare,
  reportArea as reportSquareArea,
  reportPerimeter as reportSquarePerimeter,
} from "./modules/square.js";

import {
  name as circleName,
  draw as drawCircle,
  reportArea as reportCircleArea,
  reportPerimeter as reportCirclePerimeter,
} from "./modules/circle.js";

import {
  name as triangleName,
  draw as drawTriangle,
  reportArea as reportTriangleArea,
  reportPerimeter as reportTrianglePerimeter,
} from "./modules/triangle.js";
```

Обратите внимание, что вместо этого вы можете решить проблему в файлах модуля, например.

```js
// внутри square.js
export {
  name as squareName,
  draw as drawSquare,
  reportArea as reportSquareArea,
  reportPerimeter as reportSquarePerimeter,
};
```

```js
// внутри main.js
import {
  squareName,
  drawSquare,
  reportSquareArea,
  reportSquarePerimeter,
} from "./modules/square.js";
```

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

Создание объекта модуля
-----------------------

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

```js
import \* as Module from "./modules/module.js";
```

Эта конструкция берёт все экспорты, доступные внутри `module.js` и делает их доступными в качестве свойств объекта `Module`, фактически давая ему собственное пространство имен. Так например:

```js
Module.function1();
Module.function2();
```

и т.д.

 Опять же, давайте посмотрим на реальный пример. Если вы посмотрите на нашу директорию module-objects,
 вы снова увидите тот же самый пример, но переписанный с учётом преимуществ этого нового синтаксиса.
 В модулях все экспорты представлены в следующей простой форме:

```js
export { name, draw, reportArea, reportPerimeter };
```

С другой стороны, импорт выглядит так:

```js
import \* as Canvas from "./modules/canvas.js";

import \* as Square from "./modules/square.js";
import \* as Circle from "./modules/circle.js";
import \* as Triangle from "./modules/triangle.js";
```

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

```js
let square1 = Square.draw(myCanvas.ctx, 50, 50, 100, "blue");
Square.reportArea(square1.length, reportList);
Square.reportPerimeter(square1.length, reportList);
```

Таким образом, теперь вы можете написать код точно так же, как и раньше (при условии, что вы включаете имена объектов там, где это необходимо), и импорт будет намного более аккуратным.

Модули и классы
---------------

Как мы намекали ранее, вы также можете экспортировать и импортировать классы — это ещё один способ избежать конфликтов в вашем коде, и он особенно полезен, если у вас уже есть код модуля, написанный в объектно-ориентированном стиле.

Вы можете увидеть пример нашего модуля для рисования фигур, переписанного с помощью классов ES в нашей директории classes. В качестве примера, файл `square.js` теперь содержит всю свою функциональность в одном классе:

```js
class Square {
  constructor(ctx, listId, length, x, y, color) {
    ...
  }

  draw() {
    ...
  }

  ...
}
```

который мы затем экспортируем:

```js
export { Square };
```

Далее в `main.js`, мы импортируем его так:

```js
import { Square } from "./modules/square.js";
```

А затем используем импортированный класс, чтобы нарисовать наш квадрат:

```js
let square1 = new Square(myCanvas.ctx, myCanvas.listId, 50, 50, 100, "blue");
square1.draw();
square1.reportArea();
square1.reportPerimeter();
```

Агрегирующие модули
-------------------

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

```js
export \* from "x.js";
export { name } from "x.js";
```

Для примера посмотрите на нашу директорию module-aggregation. В этом примере (на основе нашего предыдущего примера с классами) у нас есть дополнительный модуль с именем `shapes.js`, который собирает функциональность `circle.js`, `square.js` и `triangle.js` вместе. Мы также переместили наши подмодули в дочернюю директорию внутри директории `modules` под названием `shape`. Итак, структура модуля в этом примере:
```

modules/
  canvas.js
  shapes.js
  shapes/
    circle.js
    square.js
    triangle.js
```

В каждом из подмодулей экспорт имеет одинаковую форму, например:

```js
export { Square };
```

 Далее идет агрегирование.
 Внутри `shapes.js`, мы добавляем следующие строки:

```js
export { Square } from "./shapes/square.js";
export { Triangle } from "./shapes/triangle.js";
export { Circle } from "./shapes/circle.js";
```

Они берут экспорт из отдельных подмодулей и фактически делают их доступными из модуля `shape.js`.

**Примечание:** Экспорты, указанные в `shape.js`, по сути перенаправляются через файл и на самом деле там не существуют, поэтому вы не сможете написать какой-либо полезный связанный код внутри того же файла.

Итак, теперь в файле `main.js` мы можем получить доступ ко всем трём классам модулей, заменив:

```js
import { Square } from "./modules/square.js";
import { Circle } from "./modules/circle.js";
import { Triangle } from "./modules/triangle.js";
```

на единственную строку кода:

```js
import { Square, Circle, Triangle } from "./modules/shapes.js";
```

Динамическая загрузка модулей
-----------------------------

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

 Поддержка динамической загрузки модулей позволяет вызывать `import()` в качестве функции, передав ей аргументом путь к модулю.
 Данный вызов возвращает `Promise`, который резолвится объектом модуля (см. Создание объекта модуля), предоставляя вам доступ к экспорту указанного модуля, например:

```js
import("./modules/myModule.js").then((module) => {
  // Делаем что-нибудь с импортированным модулем
});
```

 Давайте посмотрим на пример.
 В директории dynamic-module-imports у нас есть ещё один пример, основанный на примере наших классов.
 Однако на этот раз мы ничего не рисуем на холсте при загрузке страницы.
 Вместо этого мы добавляем на страницу три кнопки — «Circle», «Square» и «Triangle», которые при нажатии динамически загружают требуемый модуль, а затем используют его для рисования указанной фигуры.

В этом примере мы внесли изменения только в наши `index.html` и `main.js` — экспорт модуля остается таким же, как и раньше.

Далее в `main.js` мы взяли ссылку на каждую кнопку, используя вызов `document.querySelector()`:

```js
let squareBtn = document.querySelector(".square");
```

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

```js
squareBtn.addEventListener("click", () => {
  import("./modules/square.js").then((Module) => {
    let square1 = new Module.Square(
      myCanvas.ctx,
      myCanvas.listId,
      50,
      50,
      100,
      "blue",
    );
    square1.draw();
    square1.reportArea();
    square1.reportPerimeter();
  });
});
```

Обратите внимание: поскольку выполнение Promise возвращает объект модуля, класс затем становится подкомпонентом объекта, поэтому теперь для получения доступа к конструктору нам нужно добавить к нему `Module.`, например `Module.Square(...)`.

Устранение проблем
------------------

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

* Мы упоминали об этом раньше, но повторяем:`.js`-файлы должны быть загружены с MIME-type равным `text/javascript`
 (или любым другим JavaScript-совместимым MIME-type, но `text/javascript` является рекомендованным),
 в противном случае вы получите ошибку проверки MIME-type, например — "The server responded with a non-JavaScript MIME type".
* Если вы попытаетесь загрузить HTML-файл локально (то есть по ссылке `file://`), вы столкнетесь с ошибками CORS из-за требований безопасности JavaScript-модулей.
 Вам нужно проводить тестирование через сервер.
 GitHub pages идеально для этого подходят, так как отдают `.js`-файлы с нужным MIME-type.
* Поскольку `.mjs` — нестандартное расширение файла, некоторые операционные системы могут его не распознать или попытаться заменить на что-то другое.
 Например, мы обнаружили, что macOS незаметно добавляла `.js` в конец файлов `.mjs`, а затем автоматически скрывала расширение файла.
 Таким образом, все наши файлы на самом деле имели название типа `x.mjs.js`.
 Когда мы отключили автоматическое скрытие расширений файлов и научили macOS принимать `.mjs`, всё стало в порядке.
Смотрите также
--------------

* Использование JavaScript-модулей в вебе, от Эдди Османи и Матиаса Байненса (англ.)
* Глубокое погружение в ES-модули в картинках, от Лина Кларка на Hacks blog (англ.)
* Глубокое погружение в ES-модули в картинках, перевод на русский язык от «Веб-стандартов»
* ES6 в деталях: Модули, статья от Джейсона Орендорфа на Hacks blog (англ.)
* Изучаем JS: Модули (англ), книга Акселя Раушмайера (англ.)
* « Предыдущая статья


Работа с объектами - JavaScript

Работа с объектами
==================

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

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

Обзор объектов
--------------

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

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

Объекты и свойства
------------------

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

```js
objectName.propertyName;
```

Как и все переменные JavaScript, имя объекта (которое тоже может быть переменной) и имя свойства являются чувствительными к регистру. Вы можете определить свойство указав его значение. Например, давайте создадим объект `myCar` и определим его свойства `make`, `model`, и `year` следующим образом:

```js
var myCar = new Object();
myCar.make = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;
```

Неопределённые свойства объекта являются `undefined` (а не `null`).

```js
myCar.color; // undefined
```

Свойства объектов JavaScript также могут быть доступны или заданы с использованием скобочной записи (более подробно см. property accessors). Объекты иногда называются *ассоциативными массивами*, поскольку каждое свойство связано со строковым значением, которое можно использовать для доступа к нему. Так, например, вы можете получить доступ к свойствам объекта `myCar` следующим образом:

```js
myCar["make"] = "Ford";
myCar["model"] = "Mustang";
myCar["year"] = 1969;
```

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

```js
var myObj = new Object(),
  str = "myString",
  rand = Math.random(),
  obj = new Object();

myObj.type = "Dot syntax";
myObj["date created"] = "String with space";
myObj[str] = "String value";
myObj[rand] = "Random Number";
myObj[obj] = "Object";
myObj[""] = "Even an empty string";

console.log(myObj);
```

Обратите внимание, что все ключи с квадратными скобками преобразуются в тип String, поскольку объекты в JavaScript могут иметь в качестве ключа только тип String. Например, в приведённом выше коде, когда ключ `obj` добавляется в `myObj`, JavaScript вызывает метод `obj.toString ()` и использует эту результирующую строку в качестве нового ключа.

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

```js
var propertyName = "make";
myCar[propertyName] = "Ford";

propertyName = "model";
myCar[propertyName] = "Mustang";
```

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

```js
function showProps(obj, objName) {
  var result = "";
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
      result += objName + "." + i + " = " + obj[i] + "\n";
    }
  }
  return result;
}
```

Так что если вызвать эту функцию вот так `showProps(myCar, "myCar"),` то получим результат:

```js
myCar.make = Ford;
myCar.model = Mustang;
myCar.year = 1969;
```

Перечисление всех свойств объекта
---------------------------------

Начиная с ECMAScript 5, есть три способа перечислить все свойства объекта (получить их список):

* циклы for...in (en-US)
 Этот метод перебирает все перечисляемые свойства объекта и его цепочку прототипов
* Object.keys(o) (en-US)
 Этот метод возвращает массив со всеми собственными (те, что в цепочке прототипов, не войдут в массив) именами перечисляемых свойств объекта `o`.
* Object.getOwnPropertyNames(o) (en-US)
 Этот метод возвращает массив содержащий все имена своих свойств (перечисляемых и неперечисляемых) объекта `o`.

До ECMAScript 5 не было встроенного способа перечислить все свойства объекта. Однако это можно сделать с помощью следующей функции:

```js
function listAllProperties(o) {
  var objectToInspect;
  var result = [];

  for (
    objectToInspect = o;
    objectToInspect !== null;
    objectToInspect = Object.getPrototypeOf(objectToInspect)
  ) {
    result = result.concat(Object.getOwnPropertyNames(objectToInspect));
  }

  return result;
}
```

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

Создание новых объектов
-----------------------

JavaScript содержит набор встроенных объектов. Также вы можете создавать свои объекты. Начиная с JavaScript 1.2, вы можете создавать объект с помощью инициализатора объекта. Другой способ — создать функцию-конструктор и сделать экземпляр объекта с помощью этой функции и оператора `new`.

### Использование инициализаторов объекта

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

```js
var obj = {
  property\_1: value_1, // property\_# may be an identifier...
  2: value_2, // or a number...
  // ...,
  "property n": value_n,
}; // or a string
```

здесь `obj` — это имя нового объекта, каждое `property_i` — это идентификатор (имя, число или строковый литерал), и каждый `value_i` — это значения, назначенные *`property_i`*. Имя `obj` и ссылка объекта на него необязательна; если далее вам не надо будет ссылаться на данный объект, то вам не обязательно назначать объект переменной. (Обратите внимание, что вам потребуется обернуть литерал объекта в скобки, если объект находится в месте, где ожидается инструкция, чтобы интерпретатор не перепутал его с блоком.)

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

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

```js
if (cond) var x = { hi: "there" };
```

Следующий пример создаёт объект `myHonda` с тремя свойствами. Заметьте, что свойство `engine` — это также объект со своими собственными свойствами.

```js
var myHonda = {
  color: "red",
  wheels: 4,
  engine: {
    cylinders: 4,
    size: 2.2,
  },
};
```

Вы также можете использовать инициализатор объекта для создания массивов. Смотрите array literals.

До JavaScript 1.1 не было возможности пользоваться инициализаторами объекта. Единственный способ создавать объекты — это пользоваться функциями-конструкторами или функциями других объектов, предназначенных для этой цели. Смотрите Using a constructor function.

### Использование функции конструктора

Другой способ создать объект в два шага описан ниже:

1. Определите тип объекта, написав функцию-конструктор. Название такой функции, как правило, начинается с заглавной буквы.
2. Создайте экземпляр объекта с помощью ключевого слова `new`.

Чтобы определить тип объекта создайте функцию, которая определяет тип объекта, его имя, свойства и методы. Например предположим, что вы хотите создать тип объекта для описания машин. Вы хотите, чтобы объект этого типа назывался `car`, и вы хотите, чтобы у него были свойства make, model, и year. Чтобы сделать это, напишите следующую функцию:

```js
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
```

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

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

```js
var mycar = new Car("Eagle", "Talon TSi", 1993);
```

Эта инструкция создаёт объект типа Car со ссылкой `mycar` и присваивает определённые значения его свойствам. Значением `mycar.make` станет строка "Eagle", `mycar.year` — это целое число 1993, и так далее.

Вы можете создать столько объектов `car,` сколько нужно, просто вызывая `new`. Например:

```js
var kenscar = new Car("Nissan", "300ZX", 1992);
var vpgscar = new Car("Mazda", "Miata", 1990);
```

Объект может иметь свойство, которое будет другим объектом. Например, далее определяется объект типа `Person` следующим образом:

```js
function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
}
```

и затем создать два новых экземпляра объектов `Person` как показано далее:

```js
var rand = new Person("Rand McKinnon", 33, "M");
var ken = new Person("Ken Jones", 39, "M");
```

Затем, вы можете переписать определение `car` и включить в него свойство `owner`, которому назначить объект `person` следующим образом:

```js
function Car(make, model, year, owner) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.owner = owner;
}
```

Затем, чтобы создать экземпляры новых объектов, выполните следующие инструкции:

```js
var car1 = new Car("Eagle", "Talon TSi", 1993, rand);
var car2 = new Car("Nissan", "300ZX", 1992, ken);
```

Заметьте, что вместо того, чтобы передавать строку, литерал или целое число при создании новых объектов, в выражениях выше передаются объекты `rand` и `ken` как аргумент функции. Теперь, если вам нужно узнать имя владельца car2, это можно сделать следующим образом:

```js
car2.owner.name;
```

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

```js
car1.color = "black";
```

добавляет свойство `color` к car1, и устанавливает его значение равным "black." Как бы там ни было, это не влияет на любые другие объекты. Чтобы добавить новое свойство всем объектам одного типа, вы должны добавить свойство в определение типа объекта `car`.

### Использование метода Object.create

Объекты также можно создавать с помощью метода `Object.create`. Этот метод очень удобен, так как позволяет вам указывать объект прототип для нового вашего объекта без определения функции конструктора.

```js
// список свойств и методов для Animal
var Animal = {
  type: "Invertebrates", // Значение type по умолчанию
  displayType: function () {
    // Метод отображающий тип объекта Animal
    console.log(this.type);
  },
};

// Создаём объект Animal
var animal1 = Object.create(Animal);
animal1.displayType(); // Выведет:Invertebrates

// Создаём объект Animal и присваиваем ему type = Fishes
var fish = Object.create(Animal);
fish.type = "Fishes";
fish.displayType(); // Выведет:Fishes
```

Наследование
------------

Все объекты в JavaScript наследуются как минимум от другого объекта. Объект, от которого произошло наследование называется прототипом, и унаследованные свойства могут быть найдены в объекте `prototype` конструктора.

Индексы свойств объекта
-----------------------

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

Это ограничение налагается когда вы создаёте объект и его свойства с помощью функции конструктора (как мы это делали ранее с типом *Car* ) и когда вы определяете индивидуальные свойства явно (например, `myCar.color = "red"`). Если вы изначально определили свойство объекта через индекс, например `myCar[5] = "25 mpg"`, то впоследствии сослаться на это свойство можно только так `myCar[5]`.

Исключение из правил — объекты, отображаемые из HTML, например массив `forms`. Вы всегда можете сослаться на объекты в этих массивах или используя их индекс (который основывается на порядке появления в HTML документе), или по их именам (если таковые были определены). Например, если второй html-тег `<FORM>` в документе имеет значение атрибута `NAME` равное "myForm", вы можете сослаться на эту форму вот так: `document.forms[1]` или `document.forms["myForm"]` или `document.myForm`.

Определение свойств для типа объекта
------------------------------------

Вы можете добавить свойство к ранее определённому типу объекта воспользовавшись специальным свойством `prototype`. Через `prototype` создаётся свойство, единое для всех объектов данного типа, а не одного экземпляра этого типа объекта. Следующий код демонстрирует это, добавляя свойство `color` ко всем объектам типа `car`, а затем присваивая значение свойству `color` объекта `car1`.

```js
Car.prototype.color = null;
car1.color = "black";
```

Смотрите свойство `prototype` (en-US) объекта `Function` в Справочнике JavaScript для получения деталей.

Определение методов
-------------------

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

```js
objectName.methodname = function_name;

var myObj = {
  myMethod: function (params) {
    // ...do something
  },
};
```

где `objectName` — это существующий объект, `methodname` — это имя, которое вы присваиваете методу, и `function_name` — это имя самой функции.

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

```js
object.methodname(params);
```

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

```js
function displayCar() {
  var result = "A Beautiful " + this.year + " " + this.make + " " + this.model;
  pretty\_print(result);
}
```

где `pretty_print` — это функция отображения горизонтальной линии и строки. Заметьте, что использование `this` позволяет ссылаться на объект, которому принадлежит метод.

Вы можете сделать эту функцию методом `car,` добавив инструкцию

```js
this.displayCar = displayCar;
```

к определению объекта. Таким образом, полное определение `car` примет следующий вид:

```js
function Car(make, model, year, owner) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.owner = owner;
  this.displayCar = displayCar;
}
```

Теперь вы можете вызвать метод `displayCar` для каждого из объектов как показано ниже:

```js
car1.displayCar();
car2.displayCar();
```

Использование `this` для ссылки на объект
-----------------------------------------

В JavaScript есть специальное ключевое слово this, которое вы можете использовать внутри метода, чтобы ссылаться на текущий объект. Предположим, у вас есть функция validate, которая сверяет свойство value, переданного ей объекта с некоторыми верхним и нижним значениями:

```js
function validate(obj, lowval, hival) {
  if (obj.value < lowval || obj.value > hival) alert("Invalid Value!");
}
```

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

```html
<input type="text" name="age" size="3" onChange="validate(this, 18, 99)" />
```

В общем случае, `this` ссылается на объект, вызвавший метод.

Через `this` можно обратиться и к родительской форме элемента, воспользовавшись свойством `form`. В следующем примере форма `myForm` содержит элемент ввода `Text` и кнопку `button1`. Когда пользователь нажимает кнопку, значению объекта `Text` назначается имя формы. Обработчик событий кнопки `onclick` пользуется `this.form` чтобы сослаться на текущую форму, `myForm`.

```html
<form name="myForm">
  <p>
    <label>Form name:<input type="text" name="text1" value="Beluga" /></label>
  </p>
  <p>
    <input
 name="button1"
 type="button"
 value="Show Form Name"
 onclick="this.form.text1.value = this.form.name" />
  </p>
</form>
```

Определение геттеров и сеттеров
-------------------------------

*Геттер(от англ. get - получить)* — это метод, который получает значение определённого свойства. *Сеттер* (от англ. set — присвоить) — это метод, который присваивает значение определённому свойству объекта. Вы можете определить геттеры и сеттеры для любых из встроенных или определённых вами объектов, которые поддерживают добавление новых свойств. Синтаксис определения геттеров и сеттеров использует литеральный синтаксис объектов.

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

```js
var o = {
  a: 7,
  get b() {
    return this.a + 1;
  },
  set c(x) {
    this.a = x / 2;
  },
};

console.log(o.a); // 7
console.log(o.b); // 8
o.c = 50;
console.log(o.a); // 25
```

Объект `o` получит следующие свойства:

* `o.a` — число
* `o.b` — геттер, который возвращает `o.a` плюс 1
* `o.c` — сеттер, который присваивает значение `o.a` половине значения которое передано в `o.c`

Следует особо отметить, что имена функций, указанные в литеральной форме "[gs]et *propertyName*() { }" не будут в действительности являться именами геттера и сеттера. Чтобы задать в качестве геттера и сеттера функции с явно определёнными именами, используйте метод `Object.defineProperty` (или его устаревший аналог `Object.prototype.__defineGetter__` (en-US)).

В коде ниже показано, как с помощью геттера и сеттера можно расширить прототип объекта `Date` и добавить ему свойство `year,` которое будет работать у всех экземпляров класса `Date`. Этот код использует существующие методы класса `Date` - `getFullYear` и `setFullYear` для работы геттера и сеттера.

Определение геттера и сеттера для свойства `year`:

```js
var d = Date.prototype;
Object.defineProperty(d, "year", {
  get: function () {
    return this.getFullYear();
  },
  set: function (y) {
    this.setFullYear(y);
  },
});
```

Использование свойства `year` заданного геттером и сеттером:

```js
var now = new Date();
console.log(now.year); // 2000
now.year = 2001; // 987617605170
console.log(now);
// Wed Apr 18 11:13:25 GMT-0700 (Pacific Daylight Time) 2001
```

В принципе, геттеры и сеттеры могут быть либо:

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

Когда определение геттера и сеттера использует инициализаторы объекта, всё что вам нужно, это дополнить геттер префиксом `get` а сеттер префиксом `set`. При этом, метод геттера не должен ожидать каких либо параметров, в то время как метод сеттера принимает один единственный параметр (новое значение для присвоения свойству). Например:

```js
var o = {
  a: 7,
  get b() {
    return this.a + 1;
  },
  set c(x) {
    this.a = x / 2;
  },
};
```

Геттеры и сеттеры, могут быть добавлены существующему объекту в любой момент, при помощи метода `Object.defineProperties`. Первый параметр этого метода - объект, которому вы хотите присвоить геттер и сеттер. Второй параметр - это объект, имена свойств которого будут соответствовать именам создаваемых свойств, а значения - объекты определяющие геттер и сеттер создаваемых свойств. В следующем примере создаются в точности такие же геттер и сеттер, как и в примере выше:

```js
var o = { a: 0 };

Object.defineProperties(o, {
  b: {
    get: function () {
      return this.a + 1;
    },
  },
  c: {
    set: function (x) {
      this.a = x / 2;
    },
  },
});

o.c = 10; // Запускает сеттер, который присваивает 10 / 2 (5) свойству 'a'
console.log(o.b); // Запускает геттер, который возвращает a + 1 (тоесть 6)
```

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

Удаление свойств
----------------

Вы можете удалить свойство используя оператор `delete`. Следующий код показывает как удалить свойство.

```js
//Creates a new object, myobj, with two properties, a and b.
var myobj = new Object();
myobj.a = 5;
myobj.b = 12;

//Removes the a property, leaving myobj with only the b property.
delete myobj.a;
```

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

```js
g = 17;
delete g;
```

Смотри `[delete](Expressions_and_operators#delete)` чтобы получить дополнительную информацию.

Сравнение объектов
------------------

В JavaScript объекты имеют ссылочный тип. Два отдельных объекта никогда не будут равными, даже если они имеют равный набор свойств. Только сравнение двух ссылок на один и тот же объект вернёт true.

```js
// Две переменных ссылаются на два объекта с одинаковыми свойствами
var fruit = { name: "apple" };
var fruitbear = { name: "apple" };

fruit == fruitbear; // вернёт false
fruit === fruitbear; // вернёт false
```

```js
// Две переменные ссылаются на один общий объект
var fruit = { name: "apple" };
var fruitbear = fruit; // присвоим переменной fruitbear ссылку на объект fruit

// теперь fruitbear и fruit ссылаются на один и тот же объект
fruit == fruitbear; // вернёт true
fruit === fruitbear; // вернёт true
```

```js
fruit.name = "grape";
console.log(fruitbear); // вернёт { name: "grape" } вместо { name: "apple" }
```

Подробнее смотрите Операторы сравнения (en-US).

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

* Для детального изучения читайте подробнее об объектной модели JavaScript (en-US).
* Для изучения классов ECMAScript 2015 (новый способ определения объектов), читайте главу классы JavaScript.
* « Предыдущая статья
* Следующая статья » (en-US)