> For the complete documentation index, see [llms.txt](https://imangazaliev.gitbook.io/reference/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://imangazaliev.gitbook.io/reference/js/execution-context.md).

# Контекст выполнения

Контекст выполнения (execution context) - это окружение, в котором выполняется код.

Есть три типа контекстов функции

* Глобальный контекст выполнения. Это базовый, используемый по умолчанию контекст выполнения. Если некий код находится не внутри какой-нибудь функции, значит этот код принадлежит глобальному контексту. Глобальный контекст характеризуется наличием глобального объекта, которым, в случае с браузером, является объект `window`, и тем, что ключевое слово `this` указывает на этот глобальный объект. В программе может быть лишь один глобальный контекст.
* Контекст выполнения функции. Каждый раз, когда вызывается функция, для неё создаётся новый контекст, даже если функция вызывает саму себя (рекурсия). В программе может одновременно присутствовать множество контекстов выполнения функций. При создании нового контекста выполнения функции он проходит через определённую последовательность шагов, о которой мы поговорим ниже.
* Контекст выполнения функции `eval`. Код, выполняемый внутри функции `eval`, также имеет собственный контекст выполнения. Однако функцией `eval` пользуются очень редко, поэтому здесь мы об этом контексте выполнения говорить не будем.

**Стек вызовов** (call stack) или **стек выполнения** (execution stack) - стек для хранения контекстов вызованных функций.

**Стек** - это структура данных, в которой элементы упорядочены так, что последний элемент, который попадает в стек, выходит из него первым (LIFO: last in, first out). Стек похож на стопку книг: та книга, которую мы кладём последней, находится сверху.

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

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

Перед выполнением JavaScript-кода создаётся контекст выполнения. В процессе его создания выполняются три действия:

1. Определяется значение `this` и осуществляется привязка `this` (this binding).
2. Создаётся компонент `LexicalEnvironment` (лексическое окружение).
3. Создаётся компонент `VariableEnvironment` (окружение переменных).

Концептуально контекст выполнения можно представить так:

```js
ExecutionContext = {
  ThisBinding = <this value>,
  LexicalEnvironment = { ... },
  VariableEnvironment = { ... },
}
```

Жизненный цикл контекста имеет два этапа - этап создания и этап выполнения.

## Привязка this

В глобальном контексте выполнения `this` содержит ссылку на глобальный объект (как уже было сказано, в браузере это объект `window`).

В контексте выполнения функции значение `this` зависит от того, как именно была вызвана функция. Если она вызвана в виде метода объекта, тогда значение `this` привязано к этому объекту. В других случаях `this` привязывается к глобальному объекту или устанавливается в `undefined` (в строгом режиме).

## Лексическое окружение

**Лексическое окружение** - это структура, которая хранит сведения о соответствии идентификаторов и переменных. Под «идентификатором» здесь понимается имя переменной или функции, а под «переменной» - ссылка на конкретный объект (в том числе — на функцию) или примитивное значение.

В лексическом окружении имеется **два компонента**:

1. *Environment Record* – объект, в котором как свойства хранятся все локальные переменные (а также некоторая другая информация, такая как значение `this`).
2. Ссылка на внешнее окружение. Наличие такой ссылки говорит о том, что у лексического окружения есть доступ к родительскому лексическому окружению (области видимости).

"Переменная" – это просто свойство специального внутреннего объекта: Environment Record. «Получить или изменить переменную», означает, «получить или изменить свойство этого объекта».

Именно из-за формирования записи окружения и выделения памяти под переменные до выполнения кода к ним можно обращаться до их объявления в программе. Такое поведения называется \*\*“всплытие”\*\*или **hoisting**

Однако, важно обратить внимание на то, что для переменных `let` и `const` есть некоторые отличия от `var` переменных в механизме всплытия. Например, если обратиться к переменным `let` и `const` до их объявления, то возникнет ошибка `ReferenceError`, в отличие от переменной `var`, значение которой в таком случае отобразится как `undefined`.

Такая ошибка `ReferenceError` из-за попытки получить или установить значение `let`  или `const` переменной до её объявления называется ошибкой **“Временной мертвой зоны” (Temporal Dead Zone (TDZ) error).**

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

```js
console.log(typeof foo); // Uncaught ReferenceError: foo is not defined
console.log(typeof aVariableThatDoesNotExist); // undefined
let foo;
```

В случае с необъявленной переменной `aVariableThatDoesNotExist`, которой не существует, оператор покажет `undefined`. А в случае с объявленной `foo` возникнет ошибка, так как эта переменная объявлена, но запрошена во время действия временной мертвой зоны.

Существует **два типа лексических окружений**:

1. Глобальное окружение (или глобальный контекст выполнения) — это лексическое окружение, у которого нет внешнего окружения. Ссылка глобального окружения на внешнее окружение представлена значением `null`. В глобальном окружении (в записи окружения) доступны встроенные сущности языка (такие, как `Object`, `Array`, и так далее), которые связаны с глобальным объектом, там же находятся и глобальные переменные, определённые пользователем. Значение `this` в этом окружении указывает на глобальный объект.
2. Окружение функции, в котором, в записи окружения, хранятся переменные, объявленные пользователем. Ссылка на внешнее окружение может указывать как на глобальный объект, так и на внешнюю по отношении к рассматриваемой функции функцию.

### Блоки кода

Мы также можем использовать «простые» блоки кода `{...}`, чтобы изолировать переменные в «локальной области видимости».

```js
{
    let foo = 'foo'

    // foo
    console.log(foo)
}

// здесь также будет ошибка
console.log(foo)

{
    let foo = 'foo'
}
```

### **IIFE**

В прошлом в JavaScript не было лексического окружения на уровне блоков кода.

Так что программистам пришлось что-то придумать. И то, что они сделали, называется «immediately-invoked function expressions» (аббревиатура IIFE), что означает функцию, запускаемую сразу после объявления.

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

```js
(function() {

  let message = "Hello";

  alert(message); // Hello

})();
```

Подробнее: <https://learn.javascript.ru/closure#iife>.

## **Окружение переменных**

Окружение переменных (Variable Environment) — это тоже лексическое окружение, запись окружения которого хранит привязки, созданные посредством команд объявления переменных (`VariableStatement`) в текущем контексте выполнения.

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

В ES6 существует одно различие между компонентами `LexicalEnvironment` и `VariableEnvironment`. Оно заключается в том, что первое используется для хранения объявлений функций и переменных, объявленных с помощью ключевых слов `let` и `const`, а второе — только для хранения привязок переменных, объявленных с использованием ключевого слова `var`.

## Объект arguments

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

Можно обращаться в параметрам по их индексу, начиная с нуля. Также у объекта есть свойство `length`, которое позволяет определить количество аргументов.

```js
function () {
    console.log(arguments)
}
```

Начиная с ES6 более предпочтительным споособом по сравнению с `arguments` является синтаксис **rest parameters**:

```js
function (...args) {
    console.log(args)
}
```

## Замыкание

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

```js
let name = "John";

function sayHi() {
  alert("Hi, " + name);
}

name = "Pete";

// выведет "Hi, Pete"
sayHi();
```

## Источники

* <https://habr.com/ru/company/ruvds/blog/422089/>
* <https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0>
* <http://jsflow.org/docs/exec-context-fundamentals/>
* <https://developer.mozilla.org/ru/docs/Web/JavaScript/Closures>
