Порядок выполнения и обработка ошибок - JavaScript

Порядок выполнения и обработка ошибок
=====================================

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

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

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

Любое выражение (expression) в JavaScript является также инструкцией (statement). Чтобы получить более подробную информацию о выражениях, прочитайте Выражения и операторы.

Инструкция block
----------------

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

{ statement_1; statement_2; ... statement_n; }
```

Блок обычно используется с управляющими инструкциями (например, `if`, `for`, `while`).

```js
while (x < 10) {
  x++;
}
```

В вышеприведённом примере `{ x++; }` является блоком.

**Обратите внимание:** в JavaScript **отсутствует** область видимости блока до ECMAScript2015. Переменные, объявленные внутри блока, имеют область видимости функции (или скрипта), в которой находится данный блок, вследствие чего они сохранят свои значения при выходе за пределы блока. Другими словами, блок не создаёт новую область видимости. "Автономные" (standalone) блоки в JavaScript могут продуцировать полностью отличающийся результат, от результата в языках C или Java. Например:

```js
var x = 1;
{
  var x = 2;
}
console.log(x); // выведет 2
```

В вышеприведённом примере инструкция `var x` внутри блока находится в той же области видимости, что и инструкция `var x` перед блоком. В C или Java эквивалентный код выведет значение 1.

Начиная с ECMAScript 6, оператор `let` позволяет объявить переменную в области видимости блока. Чтобы получить более подробную информацию, прочитайте `let`.

Условные инструкции
-------------------

Условная инструкция — это набор команд, которые выполняются, если указанное условие является истинным. JavaScript поддерживает две условные инструкции: `if...else` и `switch`.

### Инструкция if...else

Используйте оператор `if` для выполнения инструкции, если логическое условия истинно. Используйте опциональный `else`, для выполнения инструкции, если условие ложно. Оператор if выглядит так:

```js
if (condition) {
  statement_1;
} else {
  statement_2;
}
```

Здесь `condition` может быть любым выражением, вычисляемым как истинное (true) или ложное (false). Чтобы получить более подробную информацию о значениях `true` и `false`, прочитайте Boolean. Если условие оценивается как `true`, то выполняется `statement_1`, в противном случае - `statement_2`. Блоки `statement_1` и `statement_2` могут быть любыми блоками, включая также вложенные инструкции `if`.

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

if (condition_1) { statement_1;} else if (condition_2) { statement_2;} else if (condition_n) { statement_n; } else { statement_last;}
```

В случае нескольких условий только первое логическое условие, которое вычислится истинным (true), будет выполнено. Используйте блок (`{ ... }`) для группировки нескольких инструкций. Применение блоков является хорошей практикой, особенно когда используются вложенные инструкции `if`:
```

if (condition) {
  statement_1_runs_if_condition_is_true;
  statement_2_runs_if_condition_is_true;
} else {
  statement_3_runs_if_condition_is_false;
  statement_4_runs_if_condition_is_false;
}
```

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

```js
if (x = y) {
  /\* ... \*/
}
```

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

```js
if ((x = y)) {
  /\* ... \*/
}
```

#### Ложные значения

Следующие значения являются ложными:

* `false`
* `undefined`
* `null`
* `0`
* `NaN`
* пустая строка ( `""` )

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

Не путайте примитивные логические значения `true` и `false` со значениями true и false объекта `Boolean`. Например:

```js
var b = new Boolean(false);
if (b) // это условие true
if (b == true) // это условие false
```

В следующем примере функция `checkData` возвращает `true`, если число символов в объекте `Text` равно трём; в противном случае функция отображает окно alert и возвращает `false`.

```js
function checkData() {
  if (document.form1.threeChar.value.length == 3) {
    return true;
  } else {
    alert(
      "Enter exactly three characters. " +
        document.form1.threeChar.value +
        " is not valid.",
    );
    return false;
  }
}
```

### Инструкция switch

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

switch (expression) {
   case label_1:
      statements_1
      [break;]
   case label_2:
      statements_2
      [break;]
   ...
   default:
      statements_default
      [break;]
}
```

Сначала производится поиск ветви `case` с меткой `label`, совпадающей со значением выражения `expression`. Если совпадение найдено, то соответствующий данной ветви код выполняется до оператора `break`, который прекращает выполнение `switch` и передаёт управление дальше. В противном случае управление передаётся необязательной ветви `default` и выполняется соответствующий ей код. Если ветвь `default` не найдена, то программа продолжит выполняться со строчки, следующей за инструкцией `switch`. По соглашению ветвь `default` является последней ветвью, но следовать этому соглашению необязательно.

Если оператор `break` отсутствует, то после выполнения кода, который соответствует выбранной ветви, начнётся выполнение кода, который следует за ней.

В следующем примере если `fruittype` имеет значение `"Bananas"`, то будет выведено сообщение `"Bananas are $0.48 a pound."` и оператор `break` прекратит выполнение `switch`. Если бы оператор `break` отсутствовал, то был бы также выполнен код, соответствующий ветви `"Cherries"`, т.е. выведено сообщение `"Cherries are $3.00 a pound."`.

```js
switch (fruittype) {
  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":
    console.log("Mangoes are $0.56 a pound.");
    break;
  case "Papayas":
    console.log("Mangoes and papayas are $2.79 a pound.");
    break;
  default:
    console.log("Sorry, we are out of " + fruittype + ".");
}
console.log("Is there anything else you'd like?");
```

Инструкции обработки исключений
-------------------------------

Инструкция `throw` используется, чтобы выбросить исключение, а инструкция `try...catch`, чтобы его обработать.

### Типы исключений

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

* Исключения ECMAScript
* `DOMException` (en-US) и `DOMError` (en-US)
### Инструкция throw

Используйте инструкцию `throw`, чтобы выбросить исключение. При выбросе исключения нужно указать выражение, содержащее значение, которое будет выброшено:
```

throw expression;
```

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

```js
throw "Error2"; // string
throw 42; // number
throw true; // boolean
throw {
  toString: function () {
    return "I'm an object!";
  },
}; // object
```

**Примечание:** Вы можете выбросить объект как исключение. Вы можете обращаться к свойствам данного объекта в блоке `catch`.

**Примечание:** В следующем примере объект `UserException` выбрасывается как исключение:

```js
function UserException(message) {
  this.message = message;
  this.name = "UserException";
}

UserException.prototype.toString = function () {
  return this.name + ': "' + this.message + '"';
};

throw new UserException("Value too high");
```

### Инструкция try...catch

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

В следующем примере вызывается функция `getMonthName`, которая возвращает название месяца по его номеру. Если месяца с указанным номером не существует, то функция выбросит исключение `"InvalidMonthNo"`, которое будет перехвачено в блоке `catch`:

```js
function getMonthName(mo) {
  mo = mo - 1; // Adjust month number for array index (1 = Jan, 12 = Dec)
  var months = [
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec",
  ];
  if (months[mo]) {
    return months[mo];
  } else {
    throw "InvalidMonthNo"; //throw keyword is used here
  }
}

try {
  // statements to try
  monthName = getMonthName(myMonth); // function could throw exception
} catch (e) {
  monthName = "unknown";
  logMyErrors(e); // pass exception object to error handler -> your own
}
```

#### Блок `catch`

Используйте блок `catch`, чтобы обработать исключения, сгенерированные в блоке `try`.
```

catch (catchID) { statements }
```

JavaScript создаёт идентификатор `catchID`, которому присваивается перехваченное исключение, при входе в блок `catch`; данный идентификатор доступен только в пределах блока `catch` и уничтожается при выходе из него.

В следующем примере выбрасывается исключение, которое перехватывается в блоке `catch`:

```js
try {
  throw "myException";
} catch (e) {
  console.error(e);
}
```

#### Блок `finally`

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

В следующем примере открывается файл, затем в блоке `try` происходит вызов функции `writeMyFile`, который может выбросить исключение. Если возникает исключение, то оно обрабатывается в блоке `catch`. В любом случае файл будет закрыт функцией `closeMyFile`, вызов которой находится в блоке `finally`.

```js
openMyFile();
try {
  writeMyFile(theData);
} catch (e) {
  handleError(e);
} finally {
  closeMyFile();
}
```

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

```js
function f() {
  try {
    console.log(0);
    throw "bogus";
  } catch (e) {
    console.log(1);
    return true; // приостанавливается до завершения блока `finally`
    console.log(2); // не выполняется
  } finally {
    console.log(3);
    return false; // заменяет предыдущий `return`
    console.log(4); // не выполняется
  }
  // `return false` выполняется сейчас
  console.log(5); // не выполняется
}
f(); // отображает 0, 1, 3 и возвращает `false`
```

Замена возвращаемых значений блоком `finally` распространяется в том числе и на исключения, которые выбрасываются или перевыбрасываются в блоке `catch`:

```js
function f() {
  try {
    throw "bogus";
  } catch (e) {
    console.log('caught inner "bogus"');
    throw e; // приостанавливается до завершения блока `finally`
  } finally {
    return false; // заменяет предыдущий `throw`
  }
  // `return false` выполняется сейчас
}

try {
  f();
} catch (e) {
  // Не выполняется, т.к. `throw` в `catch `заменяется на `return` в `finally`
  console.log('caught outer "bogus"');
}

// В результате отображается сообщение caught inner "bogus"
// и возвращается значение `false`
```

#### Вложенные инструкции `try...catch`

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

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

В зависимости от типа ошибки вы можете использовать свойства `name` и `message`, чтобы получить более подробную информацию. Свойство `name` содержит название ошибки (например, `DOMException` или `Error`), свойство `message` — описание ошибки.

Если вы выбрасываете собственные исключения, то чтобы получить преимущество, которое предоставляют эти свойства (например, если ваш блок `catch` не делает различий между вашими исключениями и системными), используйте конструктор `Error`. Например:

```js
function doSomethingErrorProne() {
  if (ourCodeMakesAMistake()) {
    throw new Error("The message");
  } else {
    doSomethingToGetAJavascriptError();
  }
}

try {
  doSomethingErrorProne();
} catch (e) {
  console.log(e.name); // 'Error'
  console.log(e.message); // 'The message' или JavaScript error message
}
```

Объект Promise
--------------

Начиная с ECMAScript2015, JavaScript поддерживает объект `Promise`, который используется для отложенных и асинхронных операций.

Объект `Promise` может находиться в следующих состояниях:

* *ожидание (pending)*: начальное состояние, не выполнено и не отклонено.
* *выполнено (fulfilled)*: операция завершена успешно.
* *отклонено (rejected)*: операция завершена с ошибкой.
* *заданный (settled)*: промис выполнен или отклонен, но не находится в состоянии ожидания.

![](/ru/docs/Web/JavaScript/Guide/Control_flow_and_error_handling/promises.png)

### Загрузка изображения при помощи XHR

Простой пример использования объектов `Promise` и `XMLHttpRequest` для загрузки изображения доступен в репозитории MDN promise-test на GitHub. Вы также можете посмотреть его в действии. Каждый шаг прокомментирован, что позволяет вам разобраться в архитектуре `Promise` и XHR. Здесь приводится версия без комментариев:

```js
function imgLoad(url) {
  return new Promise(function (resolve, reject) {
    var request = new XMLHttpRequest();
    request.open("GET", url);
    request.responseType = "blob";
    request.onload = function () {
      if (request.status === 200) {
        resolve(request.response);
      } else {
        reject(
          Error(
            "Image didn't load successfully; error code:" + request.statusText,
          ),
        );
      }
    };
    request.onerror = function () {
      reject(Error("There was a network error."));
    };
    request.send();
  });
}
```

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


Грамматика и типы - JavaScript

Грамматика и типы
=================

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

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

Основы
------

JavaScript заимствует большую часть синтаксиса из Java, но также испытал влияние таких языков, как Awk, Perl и Python.

JavaScript **чувствителен к регистру** и использует кодировку символов **Unicode**. Например, слово Früh ("рано" по-немецки) может использоваться в качестве имени переменной.

```js
var Früh = "foobar";
```

Но, переменная `früh` не то же самое что `Früh` потому что JavaScript чувствителен к регистру.

В JavaScript инструкции называются statements и разделяются точкой с запятой (;). Пробел (space), табуляция (tab) и перевод строки (newline) называются пробельными символами (whitespace). Исходный текст скриптов на JavaScript сканируется слева направо и конвертируется в последовательность входных элементов, являющихся токенами (tokens), управляющими символами, символами конца строки, комментариями или пробельными символами. ECMAScript также определяет некоторые ключевые слова и литералы и устанавливает правила для автоматической вставки точек с запятой (ASI), чтобы обозначить конец инструкций (statements). Однако, рекомендуется всегда ставить точку с запятой в конце каждой инструкции вручную, чтобы избежать побочных эффектов. Чтобы получить более подробную информацию, прочитайте Lexical Grammar.

Комментарии
-----------

Синтаксис **комментариев** является таким же, как и в C++ и во многих других языках:

```js
// Комментарий, занимающий одну строку.

/\* Комментарий,
 занимающий несколько строк.
 \*/

/\* Нельзя вкладывать /\* комментарий в комментарий \*/ SyntaxError \*/
```

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

В JavaScript существует три вида объявлений:

**`var`**

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

**`let`**

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

**`const`**

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

### Переменные

Вы можете использовать переменные как символические имена для значений в вашем приложении. Имена переменных называются identifiers и должны соответствовать определённым правилам.

Идентификатор в JavaScript должен начинаться с буквы, нижнего подчёркивания (\_) или знака доллара ($); последующие символы могут также быть цифрами (0-9). Поскольку JavaScript чувствителен к регистру, буквы включают символы от "A" до "Z" (верхний регистр) и символы от "a" до "z" (нижний регистр).

Вы можете использовать в идентификаторах буквы ISO 8859-1 или Unicode, например, å или ü. Вы также можете использовать управляющие последовательности Unicode как символы в идентификаторах.

Некоторые примеры корректных имён: `Number_hits`, `temp99`, `_name`.

### Объявление переменных

Вы можете объявить переменную тремя способами:

* Используя ключевое слово `var`. Например, `var x = 42`. Данный синтаксис может быть использован для объявления как локальных, так и глобальных переменных.
* Просто присвоить переменной значение. Например, `x = 42`. Переменные, объявленные данным способом, являются глобальными. Такое объявление генерирует строгое предупреждение (strict mode). Не рекомендуется использовать данный способ.
* Используя ключевое слово `let`. Например, `let y = 13`. Данный синтаксис может быть использован для объявления локальной переменной в области видимости блока.
### Присваивание значений

Переменная, объявленная через `var` или `let` без присвоения начального значения, имеет значение `undefined`.

При попытке доступа к необъявленной переменной или переменной до её объявления будет выброшено исключение `ReferenceError`:

```js
var a;
console.log("The value of a is " + a); //Значение переменной a undefined

console.log("The value of b is " + b); //Uncaught ReferenceError: b не определена

console.log("The value of c is " + c); //Значение переменной c undefined
var c;

console.log("The value of x is " + x); //Uncaught ReferenceError: x не определена
let x;
```

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

```js
var input;
if (input === undefined) {
  doThis();
} else {
  doThat();
}
```

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

```js
var myArray = [];
if (!myArray[0]) {
  myFunction();
}
```

Значение `undefined` конвертируется в `NaN`, когда используется в числовом контексте:

```js
var a;
a + 2; // NaN
```

Значение `null` ведёт себя как 0 в числовом контексте и как `false` в логическом контексте:

```js
var n = null;
console.log(n \* 32); // В консоль выведется 0
```

### Область видимости переменных

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

До ECMAScript 6 в JavaScript отсутствовала область видимости блока; переменная, объявленная внутри блока, является локальной для \_**функции** \_(или *глобальной* области видимости), внутри которой находится данный блок. Например, следующий код выведет значение 5, т.к. областью видимости переменной `x` является функция (или глобальный контекст), внутри которой объявлена переменная `x`, а ***не блок**,* которым в данном случае является оператор `if`:

```js
if (true) {
  var x = 5;
}
console.log(x); // 5
```

Такое поведение меняется, если используется оператор `let`, введённый в ECMAScript 6:

```js
if (true) {
  let y = 5;
}
console.log(y); // ReferenceError
```

### Поднятие переменных

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

```js
/\*
 \* Example 1
 \*/
console.log(x === undefined); // true
var x = 3;

/\*
 \* Example 2
 \*/
var myvar = "my value";

(function () {
  console.log(myvar); // undefined
  var myvar = "local value";
})();
```

Приведённые выше примеры будут интерпретироваться так же, как:

```js
/\*
 \* Example 1
 \*/
var x;
console.log(x === undefined); // true
x = 3;

/\*
 \* Example 2
 \*/
var myvar = "my value";

(function () {
  var myvar;
  console.log(myvar); // undefined
  myvar = "local value";
})();
```

Из-за поднятия переменных, все операторы `var` в функции следует размещать настолько близко к началу функции, насколько это возможно. Следование этому правилу улучшает ясность кода.

В ECMAScript 2015, `let (const)` **не будет подниматься** вверх блока. Однако, ссылки на переменную в блоке до объявления переменной вызовут `ReferenceError``.` Переменная во "временной мёртвой зоне" в начале блока, до объявления.

```js
function do\_something() {
  console.log(foo); // ReferenceError
  let foo = 2;
}
```

### Поднятие функций

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

```js
/\* Определение функции \*/
foo(); // "bar"

function foo() {
  console.log("bar");
}

/\* Определение функции через выражение \*/
baz(); // TypeError: baz is not a function

var baz = function () {
  console.log("bar2");
};
```

### Глобальные переменные

Глобальные переменные на самом деле являются свойствами *глобального объекта*. На веб-страницах глобальным объектом является `window`, поэтому вы можете устанавливать глобальные переменные и обращаться к ним, используя синтаксис `window.variable`:

```js
window.foo = "bar";
```

Следовательно, вы можете обращаться к глобальным переменным, объявленным в одном объекте window или frame из другого объекта window или frame, указав имя window или frame. Например, если переменная `phoneNumber` объявлена в документе, то вы можете сослаться на эту переменную из iframe как `parent.phoneNumber`.

### Константы

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

```js
const PREFIX = "212";
```

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

Правила, касающиеся области видимости, для констант являются такими же, как и для переменных, объявленных через `let`. Если ключевое слово `const` не указано, то идентификатор будет являться переменной.

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

```js
// Это вызовет ошибку
function f() {}
const f = 5;

// Это тоже вызовет ошибку
function f() {
  const g = 5;
  var g;

  // какие-то выражения
}
```

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

```js
const MY\_OBJECT = { key: "value" };
MY\_OBJECT.key = "otherValue";
```

Структуры и типы данных
-----------------------

### Типы данных

Последний стандарт ECMAScript определяет семь типов данных:

* Шесть типов данных, которые являются примитивами:
    + Boolean. `true` и `false`.
    + null. Специальное ключевое слово, обозначающее нулевое или «пустое» значение. Поскольку JavaScript чувствителен к регистру, `null` не то же самое, что `Null`, `NULL` или любой другой вариант.
    + undefined. Свойство глобального объекта; переменная, не имеющая присвоенного значения, обладает типом `undefined`.
    + Number. `42` или `3.14159`.
    + String. "Howdy".
    + Symbol (en-US) (ECMAScript 6)
* и Object

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

### Преобразование типов данных

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

```js
var answer = 42;
```

А позже вы можете присвоить этой переменной строковое значение, например:

```js
answer = "Thanks for all the fish...";
```

Поскольку JavaScript является динамически типизированным, это присваивание не вызовет сообщения об ошибке.

В выражениях, включающих числовые и строковые значения с оператором `+`, JavaScript преобразует числовые значения в строковые. Например:

```js
x = "The answer is " + 42; // "The answer is 42"
y = 42 + " is the answer"; // "42 is the answer"
```

В выражениях с другими операторами JavaScript не преобразует числовые значения в строковые. Например:

```js
"37" - 7; // 30
"37" + 7; // "377"
```

### Преобразование строк в числа

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

* `parseInt(numString, [radix])`
* `parseFloat(numString)`

`parseInt` преобразует строку в целочисленное значение. Хорошей практикой является всегда указывать основание системы счисления (параметр `radix`).

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

```js
"1.1" + "1.1"; // "1.11.1"
(+"1.1") + (+"1.1"); // 2.2
// Обратите внимание на то, что скобки не являются обязательными и используются для ясности.
```

Литералы
--------

Литералы используются для представления значений в JavaScript. Они являются фиксированными значениями, а не переменными. В данной секции рассматриваются следующие типы литералов:

* Литерал массива
* Логический литерал
* Литерал целого числа
* Литерал числа с плавающей точкой
* Литерал объекта
* RegExp литерал
* Литерал строки
### Литерал массива

Литерал массива — это список из нуля или более выражений, каждое из которых представляет элемент массива, заключённый в квадратные скобки ( `[]` ). Когда вы создаёте массив, используя литерал массива, он инициализируется с помощью переданных значений, которые будут являться его элементами, длина массива будет равна числу переданных аргументов.

В следующем примере создаётся массив `coffees` с тремя элементам и длиной, равной трём:

```js
var coffees = ["French Roast", "Colombian", "Kona"];
```

**Примечание:** Обратите внимание на то, что литерал массива является инициализатором объекта. Чтобы получить более подробную информацию, прочитайте Использование инициализаторов объекта.

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

Литералы массива также являются объектами `Array`. Чтобы получить более подробную информацию, прочитайте `Array` и упорядоченные наборы данных.

#### Лишние запятые в литералах array

Не обязательно указывать все элементы в литерале array. Если вы поставите две запятые подряд, то пропущенные элементы будут иметь значение `undefined`. Например:

```js
var fish = ["Lion", , "Angel"]; // ["Lion", undefined, "Angel"]
```

У этого массива есть 2 элемента со значениям и один пустой (`fish[0]` - "Lion", `fish[1]` - `undefined`, а `fish[2]` - "Angel").

Если вы поставите запятую в конце списка элементов, то она будет проигнорирована. В следующем примере, длина массива равна 3. Нет `myList[3]`. Все другие запятые в списке говорят о новом элементе.

**Примечание:** Лишние запятые могут вызывать ошибки в старых версиях браузеров, поэтому лучше избегать их использования.

```js
var myList = ["home", , "school", ]; // ["home", undefined, "school"]
```

В следующем примере длина массива равна четырём, элементы `myList[0]` и `myList[2]` имеют значение `undefined`:

```js
var myList = [, "home", , "school"]; // [undefined, "home", undefined, "school"]
```

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

```js
var myList = ["home", , "school", ,]; // ["home", undefined, "school", undefined]
```

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

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

Логический (Boolean) тип имеет два литеральных значения: `true` и `false`.

Не путайте примитивные логические значения `true` и `false` со значениями true и false объекта Boolean. Объект Boolean является объектом-обёрткой над примитивом логического типа. Чтобы получить более подробную информацию, прочитайте `Boolean`.

### Литерал целого числа

Целые числа могут быть записаны в десятичной, шестнадцатеричной, восьмеричной и двоичной системах счисления.

* Десятичный целочисленный литерал состоит из последовательности цифр без ведущего нуля.
* Ведущий ноль в целочисленном литерале указывает на то, что он записан в восьмеричной системе счисления. Восьмеричные целые числа состоят только из цифр 0-7.
* Ведущие символы 0x (или 0X) указывают на то, что число шестнадцатеричное. Шестнадцатеричные целые числа могут состоять из цифр 0-9 и букв a-f и A-F.
* Ведущие символы 0b (или 0B) указывают на то, что число двоичное. Двоичные числа могут включать в себя только цифры 0 и 1.

Несколько примеров целочисленных литералов:
```

0, 117 и -345 (десятичная система счисления)
015, 0001 и -077 (восьмеричная система счисления)
0x1123, 0x00111 и -0xF1A7 (шестнадцатеричная система счисления)
0b11, 0b0011 и -0b11 (двоичная система счисления)
```

Для дополнительной информации смотрите числовые литералы в лексической грамматике.

### Литерал числа с плавающей точкой

Числа с плавающей точкой могут состоять из следующих частей:

* Десятичное целое число, которое может иметь знак (символ "+" или "-", стоящий перед числом),
* Десятичная точка ("."),
* Дробная часть (другое десятичное число),
* Экспонента.

Экспонента состоит из символа "e" или "E", за которым следует целое число, которое может иметь знак. Число с плавающей точкой должно состоять по крайней мере из одной цифры и либо десятичной точки, либо символа "e" (или "E").

В более сжатой форме синтаксис выглядит следующим образом:
```

[(+|-)][digits][.digits][(E|e)[(+|-)]digits]
```

Примеры:
```

3.14
-3.1E+12
-.3333333333333333333
.1e-23
```

### Литерал объекта

Литерал объекта — это список из нуля или более пар, состоящих из имён свойств и связанных с ними значений, заключённый в фигурные скобки ( `{}` ). Вам не следует использовать литерал объекта в начале выражения, т.к. это приведёт к ошибке или к поведению, которого вы не ожидаете, потому что символ "{" будет интерпретироваться как начало блока.

В следующем примере свойству `myCar` объекта `car` присваивается строка `"Saturn"`, свойству `getCar` — результат вызова функции `CarTypes("Honda")`, свойству `special` — значение переменной `Sales`:

```js
var Sales = "Toyota";

function CarTypes(name) {
  if (name == "Honda") {
    return name;
  } else {
    return "Извините, мы не продаём " + name + ".";
  }
}

var car = { myCar: "Saturn", getCar: CarTypes("Honda"), special: Sales };

console.log(car.myCar); // Saturn
console.log(car.getCar); // Honda
console.log(car.special); // Toyota
```

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

```js
var car = { manyCars: { a: "Saab", b: "Jeep" }, 7: "Mazda" };

console.log(car.manyCars.b); // Jeep
console.log(car[7]); // Mazda
```

Именем свойства объекта может быть любая строка, в том числе пустая строка. Если имя свойства не является корректным JavaScript идентификатором, то оно должно быть заключено в кавычки. Для обращения к таким именам следует использовать квадратные скобки ( `[]` ), а не точку ( `.` ):

```js
var unusualPropertyNames = {
  "": "An empty string",
  "!": "Bang!"
}
console.log(unusualPropertyNames."");   // SyntaxError: Unexpected string
console.log(unusualPropertyNames[""]);  // "An empty string"
console.log(unusualPropertyNames.!);    // SyntaxError: Unexpected token !
console.log(unusualPropertyNames["!"]); // "Bang!"
```

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

```js
var obj = {
  // \_\_proto\_\_
  \_\_proto\_\_: theProtoObj,
  // Короткая запись для ‘handler: handler’
  handler,
  // Методы
  toString() {
    // Супер вызовы
    return "d " + super.toString();
  },
  // Динамическое вычисление имён свойств
  ["prop\_" + (() => 42)()]: 42,
};
```

Обратите внимание на следующий пример:

```js
var foo = { a: "alpha", 2: "two" };
console.log(foo.a); // alpha
console.log(foo[2]); // two
// console.log(foo.2); // SyntaxError: Unexpected number
// console.log(foo[a]); // ReferenceError: a is not defined
console.log(foo["a"]); // alpha
console.log(foo["2"]); // two
```

### RegExp литерал

Литерал regexp - шаблон между слешами. Следующий пример литерал regex:

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

### Строковый литерал

Строковый литерал — это ноль или более символов, заключённых в двойные ( `"` ) или одинарные ( `'` ) кавычки. Строка должна быть ограничена кавычками одного типа, т.е. либо обе одинарные, либо обе двойные. Например:
```

"foo"
'bar'
"1234"
"one line \n another line"
"John's cat"
```

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

```js
console.log("John's cat".length);
// Число символов в строке, включая пробел.
// В данном случае длина строки равна 10.
```

В ES2015 также доступны шаблоны строк. Шаблоны строк представляют собой синтаксический сахар для конструирования строк. Это похоже на возможности интерполяции строк в Perl, Python и других. Дополнительно, может быть добавлен тег, позволяющий настраивать конструирование строк, избегая атак внедрения и построения структур данных высокого уровня из содержимого строки.

```js
// Простое создание строки через литерал string
`In JavaScript '\n' is a line-feed.`;

// Мультистроковые строки
`In JavaScript this is
 not legal.`;

// Интерполяция строк
var name = "Бобби",
  time = "сегодня";
`Привет ${name}, как ты ${time}?`;

// Строим префикс HTTP запроса, используемый для интерпретации замен и конструирования
POST`http://foo.org/bar?a=${a}&b=${b}
 Content-Type: application/json
 X-Credentials: ${credentials}
 { "foo": ${foo},
 "bar": ${bar}}`(myOnReadyStateChangeHandler);
```

Вам следует использовать строковые литералы до тех пор, пока вам специально не понадобится объект `String`. Чтобы получить более подробную информацию об объекте `String`, прочитайте `String`.

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

Кроме обычных символов вы также можете включать специальные символы в строки.

```js
"one line \n another line";
```

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

| Символ | Значение |
| --- | --- |
| `\b` | Возврат (Backspace) |
| `\f` | Перевод или прогон страницы (Form feed) |
| `\n` | Перевод строки (New line) |
| `\r` | Возврат каретки (Carriage return) |
| `\t` | Табуляция (Tab) |
| `\v` | Вертикальная табуляция (Vertical tab) |
| `\'` | Апостроф или одинарная кавычка |
| `\"` | Двойная кавычка |
| `\\` | Обратная косая черта (Backslash) |
| `\XXX` | Символ в кодировке Latin-1, представленный тремя восьмеричными числами *XXX* от 0 до 377. Например, \251 (символ ©). |
| `\xXX` | Символ в кодировке Latin-1, представленный двумя шестнадцатеричными числами *XX* от 00 до FF. Например, \xA9 (символ ©). |
| `\uXXXX` | Символ в Unicode, представленный четырьмя шестнадцатеричными числами *XXXX*. Например, \u00A9 (символ ©). |
| `\u{XXXXX}` | Символ в UTF-32BE. Например, \u{2F804} обозначает то же, что обычная запись \uD87E\uDC04. |

#### Экранирующие символы

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

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

```js
var quote = "He read \"The Cremation of Sam McGee\" by R.W. Service.";
console.log(quote); // He read "The Cremation of Sam McGee" by R.W. Service.
```

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

```js
var home = "c:\\temp"; // c:\temp
```

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

```js
var str =
  "this string \
is broken \
across multiple\
lines.";
console.log(str); // this string is broken across multiplelines.
```

Хотя JavaScript не поддерживает синтаксис "heredoc" (форматированный текст в одной строковой переменной), но вы можете эмулировать его, добавив перевод строки и обратную косую черту в конец каждой строки:

```js
var poem =
  "Roses are red,\n\
Violets are blue.\n\
I'm schizophrenic,\n\
And so am I.";
```

Дополнительная информация
-------------------------

Данная глава сфокусирована на базовом синтаксисе для объявлений и типов. Чтобы получить более подробную информацию о конструкциях JavaScript, прочитайте:

* Порядок выполнения и обработка ошибок
* Циклы и итерация
* Функции
* Выражения и операторы

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

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


Упорядоченные наборы данных - JavaScript

Упорядоченные наборы данных
===========================

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

Данная глава знакомит читателя с массивами - коллекциями элементов, упорядоченных по индексу. Глава включает в себя описание массивов и массивоподобных структур, таких как `Array` и `TypedArray`.

`Array` объект
--------------

*Массив* представляется собой упорядоченный набор значений, к которому вы ссылаетесь по имени и индексу. Допустим, у вас есть массив с именем `emp,` содержащий имена сотрудников и упорядоченный по номеру сотрудников. Следовательно, `emp[1]` будет представлять собой имя сотрудника номер один, `emp[2]` — имя сотрудника номер два, и т.д.

Язык JavaScript не содержит явного типа данных "массив". Тем не менее, возможно использовать предопределённый объект `Array` и его методы для работы с массивами в создаваемых приложениях. Объект `Array` содержит методы для работы с массивами самыми различными способами, например, есть методы для объединения, переворачивания и сортировки. Объект содержит свойство для определения длины массива, а также свойства для работы с регулярными выражениями.

### Создание массива

Следующие выражения создают одинаковые массивы:

```js
var arr = new Array(element0, element1, ..., elementN);
var arr = Array(element0, element1, ..., elementN);
var arr = [element0, element1, ..., elementN];
```

`element0, element1, ..., elementN` - список значений элементов массива. Если значения заданы, то эти значения будут являться элементами массива после его инициализации. Свойство `length` у массива будет равно количеству аргументов.

Синтаксис с использованием квадратных скобок называется "литерал массива" (array literal) или "инициализатор массива". Такая запись короче, чем другие способы создания массива, и, как правило, более предпочтительна. См. Array literals.

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

```js
var arr = new Array(arrayLength);
var arr = Array(arrayLength);

// Точно такой же эффект
var arr = [];
arr.length = arrayLength;
```

**Примечание:** в примере выше `arrayLength` должно иметь числовой тип `Number`. В противном случае будет создан массив с единственным элементом (указанное значение). Вызванная функция `arr.length` вернёт значение `arrayLength`, но на самом деле массив будет содержать пустые элементы (undefined). Использование цикла `for...in` для обработки значений массива не вернёт ни одного элемента.

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

```js
var obj = {};
// ...
obj.prop = [element0, element1, ..., elementN];

// OR
var obj = {prop: [element0, element1, ...., elementN]}
```

Если вы хотите инициализировать массив одним элементом и этим элементом является число типа Number, то вы должны использовать квадратные скобки. Если вы создаёте массив с помощью Array (конструктора или функции), а единственным элементом этого массива будет число типа Number, то число это интерпретируется как длина массива (arrayLength), а не как элемент типа Number.

```js
var arr = [42]; // Создаёт массив с одним элементом
var arr = Array(42); // Создаёт массив без элементов,
// но устанавливает длину массива arr.length в 42

// Это эквивалентно следующему
var arr = [];
arr.length = 42;
```

Вызов Array(N) выбросит RangeError, если N не целое значение, чья дробная часть не ноль. Следующий пример иллюстрирует это.

```js
var arr = Array(9.3); // RangeError: Invalid array length
```

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

### Заполнение массива

Вы можете заполнить массив путём присвоения значений его элементам. Для примера:

```js
var emp = [];
emp[0] = "Casey Jones";
emp[1] = "Phil Lesh";
emp[2] = "August West";
```

**Примечание:** Если вы используете нецелое значение в операторе [] обращения к элементу массива, то будет создано соответствующее свойство в объекте, представляющем массив, вместо элемента массива (так как массивы в JavaScript являются объектами).

```js
var arr = [];
arr[3.4] = "Oranges";
console.log(arr.length); // 0
console.log(arr.hasOwnProperty(3.4)); // true
```

Вы можете заполнить массив во время создания:

```js
var myArray = new Array("Hello", myVar, 3.14159);
var myArray = ["Mango", "Apple", "Orange"];
```

### Работа с элементами массива

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

```js
var myArray = ["Wind", "Rain", "Fire"];
```

Затем вы сослались на первый элемент массива как myArray[0] и второй элемент массива как myArray[1]. Индексация элементов массива начинается с **нуля**.

**Примечание:** оператор обращения к элементу массива (квадратные скобки []) также используется для доступа к свойствам массива (массивы также являются объектами в JavaScript). Например:

```js
var arr = ["one", "two", "three"];
arr[2]; // three
arr["length"]; // Вернёт число 3, так как это свойство - длина массива
```

### Понимание `length`

На уровне реализации, массивы в JavaScript хранят свои элементы как стандартные свойства объекта, используя индекс в качестве имени свойства. Специальное свойство `length` всегда возвращает индекс последнего элемента плюс один (в примере ниже, элемент 'Dusty' размещается под индексом 30, по этому cats.length возвращает 30 + 1). Особо следует запомнить, что в JavaScript массивы индексируются с нуля: отсчёт ведётся с 0, а не с 1. Из этого и следует, что свойство `length` всегда на единицу больше, чем наибольший индекс хранящийся в массиве:

```js
var cats = [];
cats[30] = ["Dusty"];
console.log(cats.length); // 31
```

Также, вы можете задавать значение для `length`. Установка значения меньшего, чем количество хранящихся в массиве элементов, обрезает массив с конца; установка `length` равным 0 очищает массив полностью:

```js
var cats = ["Dusty", "Misty", "Twiggy"];
console.log(cats.length); // 3

cats.length = 2;
console.log(cats); // выводит в консоль "Dusty,Misty" - элемент "Twiggy" был удалён

cats.length = 0;
console.log(cats); // выводит пустую строку; массив cats пуст

cats.length = 3;
console.log(cats); // выводит [undefined, undefined, undefined]
```

### Перебор содержимого массивов

Очень распространённая задача - это перебор всех элементов массива и обработка каждого элемента некоторой операцией. Вот наипростейший способ сделать это:

```js
var colors = ["red", "green", "blue"];
for (var i = 0; i < colors.length; i++) {
  console.log(colors[i]);
}
```

Если вам заранее известно, что ни один элемент массива не будет расценён как `false` при приведении к boolean — например, каждый элемент массива является DOM (en-US) узлом, тогда вы можете блеснуть чуть более эффективным оборотом:

```js
var divs = document.getElementsByTagName("div");
for (var i = 0, div; (div = divs[i]); i++) {
  /\* Обработать div некоторой операцией \*/
}
```

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

Метод `forEach()` предоставляет другой способ перебора элементов:

```js
var colors = ["red", "green", "blue"];
colors.forEach(function (color) {
  console.log(color);
});
```

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

```js
var colors = ["red", "green", "blue"];
colors.forEach((color) => console.log(color));
// red
// green
// blue
```

Функция, переданная в метод `forEach`, будет выполнена по одному разу для каждого элемента массива, при этом сам элемент массива будет передан как аргумент в эту функцию. Элементы, значения которым не присвоены, не обрабатываются `forEach` циклом.

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

```js
var array = ["first", "second", , "fourth"];

array.forEach(function (element) {
  console.log(element);
});
// first
// second
// fourth

if (array[2] === undefined) {
  console.log("array[2] is undefined"); // true
}

array = ["first", "second", undefined, "fourth"];

array.forEach(function (element) {
  console.log(element);
});
// first
// second
// undefined
// fourth
```

Так как в JavaScript элементы массива хранятся как обычные свойства объекта, использование `for...in` циклов для перебора элементов массива нежелательно, потому что будут обработаны не только элементы массива, но и все перечисляемые свойства массива.

### Методы Array

Объект `Array` имеет следующие методы:

`concat()` объединяет два массива и возвращает новый массив.
```

var myArray = new Array("1", "2", "3");
myArray = myArray.concat("a", "b", "c");
// myArray = ["1", "2", "3", "a", "b", "c"]
```

`join(deliminator = ',')` объединяет элементы массива в текстовую строку.
```

var myArray = new Array("Wind", "Rain", "Fire");
var list = myArray.join(" - "); // list = "Wind - Rain - Fire"
```

`push()` добавляет один или несколько элементов в конец массива и возвращает результирующую длину.
```

var myArray = new Array("1", "2");
myArray.push("3"); // myArray =["1", "2", "3"]
```

`pop()` удаляет из массива последний элемент и возвращает его.
```

var myArray = new Array("1", "2", "3");
var last = myArray.pop();
// myArray =["1", "2"], last = "3"
```

`shift()` удаляет из массива первый элемент и возвращает его.
```

var myArray = new Array ("1", "2", "3");
var first = myArray.shift();
// myArray = ["2", "3"], first = "1"
```

`unshift()` добавляет один или несколько элементов в начало массива и возвращает его новую длину.

```js
var myArray = new Array("1", "2", "3");
myArray.unshift("4", "5");
// myArray becomes ["4", "5", "1", "2", "3"]
```

`slice(start_index, upto_index)` возвращает секцию массива как новый массив.

```js
var myArray = new Array("a", "b", "c", "d", "e");
myArray = myArray.slice(1, 4); // начиная с индекса 1 извлекаются элементы вплоть до индекса 3
// myArray = [ "b", "c", "d"]
```

`splice(index, count_to_remove, addElement1, addElement2, ...)` удаляет часть элементов из массива и (опционально) заменяет их. Возвращает удалённые элементы.

```js
var myArray = new Array("1", "2", "3", "4", "5");
myArray.splice(1, 3, "a", "b", "c", "d");
// myArray = ["1", "a", "b", "c", "d", "5"]
// Этот код, начиная с ячейки под индексом 1 (в которой находилось значение "2"),
// удаляет 3 элемента, и вставляет на их место
// элементы, переданные в качестве последующих параметров.
```

`reverse()` переставляет элементы массива в обратном порядке: первый элемент становится последним, а последний - первым.

```js
var myArray = new Array("1", "2", "3");
myArray.reverse();
// элементы переставлены myArray = [ "3", "2", "1" ]
```

`sort()` сортирует элементы массива.

```js
var myArray = new Array("Wind", "Rain", "Fire");
myArray.sort();
// массив отсортирован myArray = [ "Fire", "Rain", "Wind" ]
```

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

Пример. Следующий код сортирует массив по последнему символу в строке:

```js
var sortFn = function (a, b) {
  if (a[a.length - 1] < b[b.length - 1]) return -1;
  if (a[a.length - 1] > b[b.length - 1]) return 1;
  if (a[a.length - 1] == b[b.length - 1]) return 0;
};
myArray.sort(sortFn);
// массив отсортирован myArray = ["Wind","Fire","Rain"]
```

* если `a` меньше чем `b` в выбранной системе сравнения, возвращаем *-1 (или любое отрицательное число)*
* если `a` больше чем `b` в выбранной системе сравнения, возвращаем *1 (или любое положительное число)*
* если `a` и `b` считаются равными, возвращаем *0*.

`indexOf(searchElement[, fromIndex])` ищет в массиве элемент со значением `searchElement` и возвращает индекс первого совпадения.

```js
var a = ["a", "b", "a", "b", "a"];
console.log(a.indexOf("b")); // выводит 1
// Попробуем ещё раз, начиная с индекса последнего совпадения
console.log(a.indexOf("b", 2)); // выводит 3
console.log(a.indexOf("z")); // выводит -1, потому что 'z' не найдено
```

`lastIndexOf(searchElement[, fromIndex])` тоже самое, что и `indexOf`, но поиск ведётся в обратном порядке, с конца массива.

```js
var a = ["a", "b", "c", "d", "a", "b"];
console.log(a.lastIndexOf("b")); // выводит 5
// Попробуем ещё раз, начиная с индекса, предшествующего индексу последнего совпадения
console.log(a.lastIndexOf("b", 4)); // выводит 1
console.log(a.lastIndexOf("z")); // выводит -1
```

`forEach(callback[, thisObject])` выполняет `callback`-функцию по каждому элементу массива.

```js
var a = ["a", "b", "c"];
a.forEach(function (element) {
  console.log(element);
});
// выводит в консоль каждый элемент массива по порядку
```

`map(callback[, thisObject])` возвращает новый массив, содержащий результаты вызова `callback`-функции для каждого элемента исходного массива.

```js
var a1 = ["a", "b", "c"];
var a2 = a1.map(function (item) {
  return item.toUpperCase();
});
console.log(a2); // выводит A,B,C
```

`filter(callback[, thisObject])` возвращает новый массив, содержащий только те элементы исходного массива, для которых вызов `callback`-функции вернул true.

```js
var a1 = ["a", 10, "b", 20, "c", 30];
var a2 = a1.filter(function (item) {
  return typeof item == "number";
});
console.log(a2); // выводит 10,20,30
```

`every(callback[, thisObject])` возвращает true, если вызов `callback`-функции вернул true для всех элементов массива.

```js
function isNumber(value) {
  return typeof value == "number";
}
var a1 = [1, 2, 3];
console.log(a1.every(isNumber)); // выводит true
var a2 = [1, "2", 3];
console.log(a2.every(isNumber)); // выводит false
```

`some(callback[, thisObject])` возвращает true, если вызов `callback`-функции вернёт true хотя бы для одного элемента.

```js
function isNumber(value) {
  return typeof value == "number";
}
var a1 = [1, 2, 3];
console.log(a1.some(isNumber)); // выводит true
var a2 = [1, "2", 3];
console.log(a2.some(isNumber)); // выводит true
var a3 = ["1", "2", "3"];
console.log(a3.some(isNumber)); // выводит false
```

Те из методов выше, что принимают `callback`-функцию в качестве аргумента, известны как методы итерации (*iterative methods)*, потому что определённым образом проходятся по всем элементам массива. Каждый из таких методов принимает второй, опциональный элемент, называемый `thisObject`. Если этот аргумент присутствует, то его значение присваивается ключевому слову `this` в теле `callback`-функции. Иначе, как и в любом другом случае вызова функции вне явного контекста, `this` будет ссылаться на глобальный объект (`window`).

В действительности `callback`-функция вызывается с тремя аргументами. Первый аргумент - текущий элемент массива, второй - индекс этого элемента, и третий - ссылка на сам массив. Однако, в JavaScript, функции игнорируют любые аргументы, которые не перечислены в списке аргументов. Таким образом, нет ничего страшного в использовании функции с одним аргументом, такой как `alert`.

`reduce(callback[, initialValue])` последовательно применяет `callback`-функцию `callback(firstValue, secondValue)` для того, чтобы свести все элементы массива к одному значению. В первый параметр функции передаётся предыдущий результат работы функции или первый элемент, а во второй - текущий элемент. Третьим параметром передаётся индекс текущего элемента.

```js
var a = [10, 20, 30];
var total = a.reduce(function (first, second, index) {
  return first + second;
}, 0);
console.log(total); // выводит 60
```

`reduceRight(callback[, initalvalue])` работает так же как и `reduce()`, но порядок обхода ведётся от конца к началу.

`Методы reduce` и `reduceRight` являются наименее очевидными методами объекта Array. Они должны использоваться в алгоритмах, которые рекурсивно совмещают два элемента массива, для сведения всей последовательности к одному значению.

### Многомерные массивы

Массивы могут быть вложенными, то есть массив может содержать массивы в элементах. Используя эту возможность массивов JavaScript, можно построить многомерные массивы.

Следующий код создаёт двумерный массив:

```js
var a = new Array(4);
for (i = 0; i < 4; i++) {
  a[i] = new Array(4);
  for (j = 0; j < 4; j++) {
    a[i][j] = "[" + i + "," + j + "]";
  }
}
```

В этом примере создаётся массив со следующим содержимым:
```

Ряд 0: [0,0] [0,1] [0,2] [0,3]
Ряд 1: [1,0] [1,1] [1,2] [1,3]
Ряд 2: [2,0] [2,1] [2,2] [2,3]
Ряд 3: [3,0] [3,1] [3,2] [3,3]
```

### Массивы и регулярные выражения

Когда массив является результатом вычислений регулярного выражения над строкой, он содержит свойства и элементы с информацией о совпадениях. Массив возвращается функциями `RegExp.exec()`, `String.match()` и `String.split()` Подробнее о том, как использовать массивы с регулярными выражениями смотрите в Regular Expressions.

### Работа с массивоподобными объектами

Некоторые объекты в JavaScript, такие как `NodeList`, возвращаемые методом `document.getElementsByTagName()`, или специальный объект `arguments`, доступный внутри функции, выглядят и ведут себя как обычные массивы, однако не имеет всех присущих массиву методов. Так например, объект `arguments` имеет свойство `length`, но не имеет метода `forEach()`.

Методы из прототипа Array, могут быть вызваны для массивоподобных объектов. Например:
```

function printArguments() {
  Array.prototype.forEach.call(arguments, function(item) {
    console.log(item);
  });
}
```

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

```js
Array.prototype.forEach.call("a string", function (chr) {
  console.log(chr);
});
```

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

JavaScript typed arrays (типизированные массивы) являются массивоподобными объектами, которые предоставляют механизм доступа к сырым бинарным данным. Как вы уже знаете, `Array` массивы динамически растут, сокращаются и могут содержать значения любых типов JavaScript. Движки JavaScript производят оптимизации, благодаря чему, эти операции происходят быстро. Однако, веб приложения становятся все более мощными, добавляются возможности манипуляции со звуковыми и видеоданными, доступ к сырым данным WebSockets, и тому подобное. Становится очевидным, что возможность быстрой и эффективной работы с двоичными данными в JavaScript будет очень полезной. Для чего типизированные массивы и предназначены.

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

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

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

### ArrayBuffer

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

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

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

Для получения подробных сведений смотрите Типизированные массивы JavaScript и справочную документацию для `TypedArray`.

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


Функции - JavaScript

Функции
=======

* « Предыдущая статья
* Следующая статья »
Функции в JavaScript
--------------------

Функции - ключевая концепция в JavaScript. Важнейшей особенностью языка является поддержка функции первого класса *(functions as first-class citizen)*. Любая **функция это объект,** и следовательно ею можно манипулировать как объектом, в частности:

* передавать как аргумент и возвращать в качестве результата при вызове других функций функций высшего порядка;
* создавать анонимно и присваивать в качестве значений переменных или свойств объектов.

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

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

Для понимания работы функций необходимо (и достаточно?) иметь представление о следующих моментах:

* способы объявления
* способы вызова
* параметры и аргументы вызова (`arguments`)
* область данных (`Scope`) и замыкания (`Closures`)
* объект привязки (`this`)
* возвращаемое значение (`return`)
* исключения (`throw`)
* использование в качестве конструктора объектов
* сборщик мусора (`garbage collector`)
Объявление функций
------------------

### Функции вида "function declaration statement"

Объявление функции (*function definition*, или *function declaration*, или *function statement*) состоит из ключевого слова `function` и следующих частей:

* Имя функции.
* Список параметров (принимаемых функцией) заключённых в круглые скобки `()` и разделённых запятыми.
* Инструкции, которые будут выполнены после вызова функции, заключают в фигурные скобки `{ }`.

Например, следующий код объявляет простую функцию с именем `square:`

```js
function square(number) {
  return number \* number;
}
```

Функция `square` принимает один параметр, названный `number.` Состоит из одной инструкции, которая означает вернуть параметр этой функции (это `number`) умноженный на самого себя. Инструкция return указывает на значение, которые будет возвращено функцией.

```js
return number \* number;
```

Примитивные параметры (например, число) передаются функции значением; **значение** передаётся в функцию, но если функция меняет значение параметра, **это изменение не отразится глобально или после вызова функции.**

Если вы передадите объект как параметр (не примитив, например, `массив` или определяемые пользователем объекты), и функция изменит свойство переданного в неё объекта, это изменение будет видно и вне функции, как показано в следующем примере:

```js
function myFunc(theObject) {
  theObject.make = "Toyota";
}

var mycar = { make: "Honda", model: "Accord", year: 1998 };
var x, y;

x = mycar.make; // x получает значение "Honda"

myFunc(mycar);
y = mycar.make; // y получает значение "Toyota"
// (свойство было изменено функцией)
```

### Функции вида "function definition expression"

Функция вида "function declaration statement" по синтаксису является инструкцией (*statement*), ещё функция может быть вида "function definition expression". Такая функция может быть **анонимной** (она не имеет имени). Например, функция `square` может быть вызвана так:

```js
var square = function (number) {
  return number \* number;
};
var x = square(4); // x получает значение 16
```

Однако, имя может быть и присвоено для вызова самой себя внутри самой функции и для отладчика (*debugger*) для идентифицированные функции в стек-треках (*stack traces*; "trace" — "след" / "отпечаток").

```js
var factorial = function fac(n) {
  return n < 2 ? 1 : n \* fac(n - 1);
};

console.log(factorial(3));
```

Функции вида "function definition expression" удобны, когда функция передаётся аргументом другой функции. Следующий пример показывает функцию `map`, которая должна получить функцию первым аргументом и массив вторым.

```js
function map(f, a) {
  var result = [], // Создаём новый массив
    i;
  for (i = 0; i != a.length; i++) result[i] = f(a[i]);
  return result;
}
```

В следующем коде наша функция принимает функцию, которая является function definition expression, и выполняет его для каждого элемента принятого массива вторым аргументом.

```js
function map(f, a) {
  var result = []; // Создаём новый массив
  var i; // Объявляем переменную
  for (i = 0; i != a.length; i++) result[i] = f(a[i]);
  return result;
}
var f = function (x) {
  return x \* x \* x;
};
var numbers = [0, 1, 2, 5, 10];
var cube = map(f, numbers);
console.log(cube);
```

Функция возвращает: [0, 1, 8, 125, 1000].

В JavaScript функция может быть объявлена с условием. Например, следующая функция будет присвоена переменной `myFunc` только, если `num` равно 0:

```js
var myFunc;
if (num === 0) {
  myFunc = function (theObject) {
    theObject.make = "Toyota";
  };
}
```

В дополнение к объявлениям функций, описанных здесь, вы также можете использовать конструктор Function для создания функций из строки во время выполнения (*runtime*), подобно `eval()`.

**Метод** — это функция, которая является свойством объекта. Узнать больше про объекты и методы можно по ссылке: Работа с объектами.

Вызовы функций
--------------

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

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

```js
square(5);
```

Эта инструкция вызывает функцию с аргументом 5. Функция вызывает свои инструкции и возвращает значение 25.

Функции могут быть в области видимости, когда они уже определены, но функции вида "function declaration statement" могут быть подняты (*поднятие* — *hoisting*), также как в этом примере:

```js
console.log(square(5));
/\* ... \*/
function square(n) {
  return n \* n;
}
```

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

**Примечание:** Это работает только тогда, когда объявлении функции использует вышеупомянутый синтаксис (т.е. `function funcName(){}`). Код ниже не будет работать. Имеется в виду то, что поднятие функции работает только с function declaration и не работает с function expression.

```js
console.log(square); // square поднят со значением undefined.
console.log(square(5)); // TypeError: square is not a function
var square = function (n) {
  return n \* n;
};
```

Аргументы функции не ограничиваются строками и числами. Вы можете передавать целые объекты в функцию. Функция `show_props()` (объявленная в Работа с объектами) является примером функции, принимающей объекты аргументом.

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

```js
function factorial(n) {
  if (n === 0 || n === 1) return 1;
  else return n \* factorial(n - 1);
}
```

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

```js
var a, b, c, d, e;
a = factorial(1); // a получает значение 1
b = factorial(2); // b получает значение 2
c = factorial(3); // c получает значение 6
d = factorial(4); // d получает значение 24
e = factorial(5); // e получает значение 120
```

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

Область видимости функций
-------------------------

***(function scope)***

Переменные объявленные в функции не могут быть доступными где-нибудь вне этой функции, поэтому переменные (которые нужны именно для функции) объявляют только в scope функции. При этом функция имеет доступ ко всем переменным и функциям, объявленным внутри её scope. Другими словами функция объявленная в глобальном scope имеет доступ ко всем переменным в глобальном scope. Функция объявленная внутри другой функции ещё имеет доступ и ко всем переменным её родительской функции и другим переменным, к которым эта родительская функция имеет доступ.

```js
// Следующие переменные объявленны в глобальном scope
var num1 = 20,
  num2 = 3,
  name = "Chamahk";

// Эта функция объявленна в глобальном scope
function multiply() {
  return num1 \* num2;
}

multiply(); // вернёт 60

// Пример вложенной функции
function getScore() {
  var num1 = 2,
    num2 = 3;

  function add() {
    return name + " scored " + (num1 + num2);
  }

  return add();
}

getScore(); // вернёт "Chamahk scored 5"
```

Scope и стек функции
--------------------

***(function stack)***

### Рекурсия

Функция может вызывать саму себя. Три способа такого вызова:

1. по имени функции
2. `arguments.callee`
3. по переменной, которая ссылается на функцию

Для примера рассмотрим следующие функцию:

```js
var foo = function bar() {
  // здесь будут выражения
};
```

Внутри функции (*function body*) все следующие вызовы эквивалентны:

1. `bar()`
2. `arguments.callee()`
3. `foo()`

Функция, которая вызывает саму себя, называется *рекурсивной функцией* (*recursive function*). Получается, что рекурсия аналогична циклу (*loop*). Оба вызывают некоторый код несколько раз, и оба требуют условия (чтобы избежать бесконечного цикла, вернее бесконечной рекурсии). Например, следующий цикл:

```js
var x = 0;
while (x < 10) {
  // "x < 10" — это условие для цикла
  // что-то делаем
  x++;
}
```

можно было изменить на рекурсивную функцию и вызовом этой функции:

```js
function loop(x) {
  if (x >= 10) {
    // "x >= 10" — это условие для конца выполнения (тоже самое, что "!(x < 10)")
    return;
  }
  // делать что-то
  loop(x + 1); // рекурсионный вызов
}
loop(0);
```

Однако некоторые алгоритмы не могут быть простыми повторяющимися циклами. Например, получение всех элементов структуры дерева (например, DOM (en-US)) проще всего реализуется использованием рекурсии:

```js
function walkTree(node) {
  if (node == null) return;
  // что-то делаем с элементами
  for (var i = 0; i < node.childNodes.length; i++) {
    walkTree(node.childNodes[i]);
  }
}
```

В сравнении с функцией `loop`, каждый рекурсивный вызов сам вызывает много рекурсивных вызовов.

Также возможно превращение некоторых рекурсивных алгоритмов в нерекурсивные, но часто их логика очень сложна, и для этого потребуется использование стека (*stack*). По факту рекурсия использует stack: function stack.

Поведение стека можно увидеть в следующем примере:

```js
function foo(i) {
  if (i < 0) return;
  console.log("begin: " + i);
  foo(i - 1);
  console.log("end: " + i);
}
foo(3);

// Output:

// begin: 3
// begin: 2
// begin: 1
// begin: 0
// end: 0
// end: 1
// end: 2
// end: 3
```

### Вложенные функции (nested functions) и замыкания (closures)

Вы можете вложить одну функцию в другую. Вложенная функция (*nested function*; *inner*) приватная (*private*) и она помещена в другую функцию (*outer*). Так образуется *замыкание* (*closure*). Closure — это выражение (обычно функция), которое может иметь свободные переменные вместе со средой, которая связывает эти переменные (что "закрывает" (*"close"*) выражение).

Поскольку вложенная функция это closure, это означает, что вложенная функция может "унаследовать" (*inherit*) аргументы и переменные функции, в которую та вложена. Другими словами, вложенная функция содержит scope внешней (*"outer"*) функции.

Подведём итог:

* Вложенная функция имеет доступ ко всем инструкциям внешней функции.

* Вложенная функция формирует closure: она может использовать аргументы и переменные внешней функции, в то время как внешняя функция не может использовать аргументы и переменные вложенной функции.

Следующий пример показывает вложенную функцию:

```js
function addSquares(a, b) {
  function square(x) {
    return x \* x;
  }
  return square(a) + square(b);
}
a = addSquares(2, 3); // возвращает 13
b = addSquares(3, 4); // возвращает 25
c = addSquares(4, 5); // возвращает 41
```

Поскольку вложенная функция формирует closure, вы можете вызвать внешнюю функцию и указать аргументы для обоих функций (для outer и innner).

```js
function outside(x) {
  function inside(y) {
    return x + y;
  }
  return inside;
}
fn_inside = outside(3); // Думайте об этом как: дайте мне функцию,
// которая добавляет 3 к любому введенному значению

result = fn\_inside(5); // возвращает 8

result1 = outside(3)(5); // возвращает 8
```

### Сохранение переменных

Обратите внимание, значение `x` сохранилось, когда возвращалось `inside`. Closure должно сохранять аргументы и переменные во всем scope. Поскольку каждый вызов предоставляет потенциально разные аргументы, создаётся новый closure для каждого вызова во вне. Память может быть очищена только тогда, когда `inside` уже возвратился и больше не доступен.

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

### Несколько уровней вложенности функций (Multiply-nested functions)

Функции можно вкладывать несколько раз, т.е. функция (A) хранит в себе функцию (B), которая хранит в себе функцию (C). Обе функции B и C формируют closures, так B имеет доступ к переменным и аргументам A, и C имеет такой же доступ к B. В добавок, поскольку C имеет такой доступ к B, который имеет такой же доступ к A, C ещё имеет такой же доступ к A. Таким образом closures может хранить в себе несколько scope; они рекурсивно хранят scope функций, содержащих его. Это называется *chaining* (*chain — цепь*; Почему названо "chaining" будет объяснено позже)

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

```js
function A(x) {
  function B(y) {
    function C(z) {
      console.log(x + y + z);
    }
    C(3);
  }
  B(2);
}
A(1); // в консоле выводится 6 (1 + 2 + 3)
```

В этом примере C имеет доступ к `y` функции `B` и к `x` функции `A`. Так получается, потому что:

1. Функция `B` формирует closure, включающее `A`, т.е. `B` имеет доступ к аргументам и переменным функции `A`.
2. Функция `C` формирует closure, включающее `B`.
3. Раз closure функции `B` включает `A`, то closure `С` тоже включает A, `C` имеет доступ к аргументам и переменным обоих функций `B` *и* `A`. Другими словами, `С` связывает *цепью* (*chain*) scopes функций `B` и `A` в таком порядке.

В обратном порядке, однако, это не верно. `A` не имеет доступ к переменным и аргументам `C`, потому что `A` не имеет такой доступ к `B`. Таким образом, `C` остаётся приватным только для `B`.

### Конфликты имён (Name conflicts)

Когда два аргумента или переменных в scope у closure имеют одинаковые имена, происходит *конфликт имени* (*name conflict*). Более вложенный (*more inner*) scope имеет приоритет, так самый вложенный scope имеет наивысший приоритет, и наоборот. Это цепочка областей видимости (*scope chain*). Самым первым звеном является самый глубокий scope, и наоборот. Рассмотрим следующие:

```js
function outside() {
  var x = 5;
  function inside(x) {
    return x \* 2;
  }
  return inside;
}

outside()(10); // возвращает 20 вместо 10
```

Конфликт имени произошёл в инструкции `return x * 2` между параметром `x` функции `inside` и переменной `x` функции `outside`. Scope chain здесь будет таким: {`inside` ==> `outside` ==> глобальный объект (*global object*)}. Следовательно `x` функции `inside` имеет больший приоритет по сравнению с `outside`, и нам вернулось 20 (= 10 \* 2), а не 10 (= 5 \* 2).

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

***(Closures)***

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

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

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

Closure создаётся, когда вложенная функция как-то стала доступной в неком scope вне внешней функции.

```js
var pet = function (name) {
  // Внешняя функция объявила переменную "name"
  var getName = function () {
    return name; // Вложенная функция имеет доступ к "name" внешней функции
  };
  return getName; // Возвращаем вложенную функцию, тем самым сохраняя доступ
  // к ней для другого scope
};
myPet = pet("Vivie");

myPet(); // Возвращается "Vivie",
// т.к. даже после выполнения внешней функции
// name сохранился для вложенной функции
```

Более сложный пример представлен ниже. Объект с методами для манипуляции вложенной функции внешней функцией можно вернуть (*return*).

```js
var createPet = function (name) {
  var sex;

  return {
    setName: function (newName) {
      name = newName;
    },

    getName: function () {
      return name;
    },

    getSex: function () {
      return sex;
    },

    setSex: function (newSex) {
      if (
        typeof newSex === "string" &&
        (newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")
      ) {
        sex = newSex;
      }
    },
  };
};

var pet = createPet("Vivie");
pet.getName(); // Vivie

pet.setName("Oliver");
pet.setSex("male");
pet.getSex(); // male
pet.getName(); // Oliver
```

В коде выше переменная `name` внешней функции доступна для вложенной функции, и нет другого способа доступа к вложенным переменным кроме как через вложенную функцию. Вложенные переменные вложенной функции являются безопасными хранилищами для внешних аргументов и переменных. Они содержат "постоянные" и "инкапсулированные" данные для работы с ними вложенными функциями. Функции даже не должны присваиваться переменной или иметь имя.

```js
var getCode = (function () {
  var apiCode = "0]Eal(eh&2"; // Мы не хотим, чтобы данный код мог быть изменен кем-то извне...

  return function () {
    return apiCode;
  };
})();

getCode(); // Возвращает apiCode
```

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

```js
var createPet = function (name) {
  // Внешняя функция определяет переменную с именем "name".
  return {
    setName: function (name) {
      // Внутренняя функция также определяет переменную с именем "name".
      name = name; // Как мы можем получить доступ к "name", определённой во внешней функции?
    },
  };
};
```

Использование объекта arguments
-------------------------------

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

```js
arguments[i];
```

где `i` — это порядковый номер аргумента, отсчитывающийся с 0. К первому аргументу, переданному функции, обращаются так `arguments[0]`. А получить количество всех аргументов — `arguments.length`.

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

Для примера рассмотрим функцию, которая конкатенирует несколько строк. Единственным формальным аргументом для функции будет строка, которая указывает символы, которые разделяют элементы для конкатенации. Функция определяется следующим образом:

```js
function myConcat(separator) {
  var result = "";
  var i;

  // iterate through arguments
  for (i = 1; i < arguments.length; i++) {
    result += arguments[i] + separator;
  }
  return result;
}
```

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

```js
// возвращает "red, orange, blue, "
myConcat(", ", "red", "orange", "blue");

// возвращает "elephant; giraffe; lion; cheetah; "
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// возвращает "sage. basil. oregano. pepper. parsley. "
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");
```

**Примечание:** `arguments` является псевдо-массивом, но не массивом. Это псевдо-массив, в котором есть пронумерованные индексы и свойство `length`. Однако он не обладает всеми методами массивов.

Рассмотрите объект `Function` в JavaScript-справочнике для большей информации.

Параметры функции
-----------------

Начиная с ECMAScript 2015 появились два новых вида параметров: параметры по умолчанию (*default parameters*) и остаточные параметры (*rest parameters*).

### Параметры по умолчанию (Default parameters)

В JavaScript параметры функции по умолчанию имеют значение `undefined`. Однако в некоторых ситуация может быть полезным поменять значение по умолчанию. В таких случаях default parameters могут быть весьма кстати.

В прошлом для этого было необходимо в теле функции проверять значения параметров на `undefined` и в положительном случае менять это значение на дефолтное (*default*). В следующем примере в случае, если при вызове не предоставили значение для `b`, то этим значением станет `undefined`, тогда результатом вычисления `a * b` в функции `multiply` будет `NaN`. Однако во второй строке мы поймаем это значение:

```js
function multiply(a, b) {
  b = typeof b !== "undefined" ? b : 1;

  return a \* b;
}

multiply(5); // 5
```

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

```js
function multiply(a, b = 1) {
  return a \* b;
}

multiply(5); // 5
```

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

### Остаточные параметры (Rest parameters)

Остаточные параметры предоставляют нам массив неопределённых аргументов. В примере мы используем остаточные параметры, чтобы собрать аргументы с индексами со 2-го до последнего. Затем мы умножим каждый из них на значение первого аргумента. В этом примере используется стрелочная функция (***Arrow functions)***, о которой будет рассказано в следующей секции.

```js
function multiply(multiplier, ...theArgs) {
  return theArgs.map((x) => multiplier \* x);
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
```

Стрелочные функции
------------------

***(Arrow functions)***

Стрелочные функции — функции вида "arrow function expression" (неверно **fat arrow function**) — имеют укороченный синтаксис по сравнению с function expression и лексически связывает значение `this`. Стрелочные функции всегда анонимны. Посмотрите также пост блога hacks.mozilla.org "ES6 In Depth: Arrow functions".

На введение стрелочных функций повлияли два фактора: более короткие функции и лексика `this`.

### Более короткие функции

В некоторых функциональных паттернах приветствуется использование более коротких функций. Сравните:

```js
var a = ["Hydrogen", "Helium", "Lithium", "Beryllium"];

var a2 = a.map(function (s) {
  return s.length;
});

console.log(a2); // выводит [8, 6, 7, 9]

var a3 = a.map((s) => s.length);

console.log(a3); // выводит [8, 6, 7, 9]
```

### Лексика `this`

До стрелочных функций каждая новая функция определяла своё значение `this` (новый объект в случае конструктора, undefined в strict mode, контекстный объект, если функция вызвана как метод объекта, и т.д.). Это оказалось раздражающим с точки зрения объектно-ориентированного стиля программирования.

```js
function Person() {
  // Конструктор Person() определяет `this` как самого себя.
  this.age = 0;

  setInterval(function growUp() {
    // Без strict mode функция growUp() определяет `this`
    // как global object, который отличается от `this`
    // определённого конструктором Person().
    this.age++;
  }, 1000);
}

var p = new Person();
```

В ECMAScript 3/5 эта проблема была исправлена путём присвоения значения `this` переменной, которую можно было бы замкнуть.

```js
function Person() {
  var self = this; // Некоторые выбирают `that` вместо `self`.
  // Выберите что-то одно и будьте последовательны.
  self.age = 0;

  setInterval(function growUp() {
    // Колбэк ссылается на переменную `self`,
    // значением которой является ожидаемый объект.
    self.age++;
  }, 1000);
}
```

Альтернативой может быть связанная функция (*bound function*), с которой можно правильно вручную определить значение `this` для функции `growUp()`.

В arrow function значением `this` является окружающий его контекст, так следующий код работает ожидаемо:

```js
function Person() {
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| должным образом ссылается на объект Person
  }, 1000);
}

var p = new Person();
```

Далее
-----

Подробное техническое описание функций в статье справочника `Функции`

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

Внешние ресурсы:

* ECMAScript® 2015 Language Specification
* Учебник по Javascript - замыкания
* « Предыдущая статья
* Следующая статья »