Что такое контекст выполнения
Контекст выполнения – это виртуальная «коробка», в которой JavaScript хранит всю информацию, необходимую для обработки конкретного куска кода. Каждый раз, когда движок начинает интерпретировать скрипт, он создает такой контейнер, заполняет его переменными, функциями и другими объектами, а затем использует его для последовательного выполнения инструкций. По сути, контекст выполнения гарантирует изоляцию данных между различными частями программы и обеспечивает предсказуемый порядок работы кода.
Фазы контекста выполнения
Контекст проходит две последовательные стадии: фаза создания (или фаза выделения памяти) и фаза исполнения.
Фаза создания (Memory Allocation)
- Сканирование кода – движок просматривает весь исходный файл, определяя, какие переменные и функции объявлены.
- Выделение памяти – для каждой переменной и функции резервируется место в памяти.
- Инициализация
var– переменные, объявленные черезvar, получают значениеundefined. - Хранение функций – функции сохраняются полностью, их тело становится доступным для последующего вызова.
- Hoisting
letиconst– переменные, объявленные черезletиconst, «поднимаются», но находятся в temporal dead zone (TDZ) до момента их фактического объявления. Обращение к ним до инициализации приводит к ошибке.
На этом этапе никакой пользовательский код не исполняется – происходит только подготовка среды.
Фаза исполнения
- Построчное выполнение – интерпретатор последовательно обрабатывает каждую инструкцию.
- Присваивание значений – переменным, объявленным ранее, присваиваются реальные данные.
- Вызов функций – при встрече вызова функции создаётся новый контекст (см. ниже) и начинается его исполнение.
- Оценка выражений – вычисляются арифметические, логические и другие операции.
Эта фаза отвечает за реальное выполнение бизнес‑логики приложения.
Типы контекстов выполнения
В JavaScript выделяют два основных типа контекстов:
Глобальный контекст выполнения (Global Execution Context, GEC)
- Создаётся в момент загрузки скрипта.
- Существует единожды за весь жизненный цикл программы.
- Охватывает глобальную область видимости (в браузере – объект
window, в Node.js –global). - Остаётся в стеке вызовов до завершения выполнения программы.
Функциональный контекст выполнения (Function Execution Context, FEC)
- Создаётся каждый раз при вызове функции.
- Имеет собственный набор переменных, параметров и ссылки на внешние контексты (замыкания).
- После завершения функции контекст удаляется из стека вызовов, освобождая память.
Каждый вызов функции порождает отдельный FEC, что позволяет рекурсии и вложенным вызовам работать независимо друг от друга.
Стек вызовов и порядок выполнения
Стек вызовов (Call Stack) – это структура данных LIFO (Last In, First Out), в которой хранятся активные контексты выполнения. При старте программы в стек помещается глобальный контекст. Когда интерпретатор встречает вызов функции, создаётся новый FEC и «набрасывается» поверх стека. После завершения функции её контекст «съедается» со стека, и управление возвращается к предыдущему контексту.
function first() {
console.log('first start');
second();
console.log('first end');
}
function second() {
console.log('second start');
third();
console.log('second end');
}
function third() {
console.log('third');
}
first();
Визуализация стека вызовов:
- GEC – запущен.
first()→ создаётся FEC‑first, помещается в стек.- Внутри
firstвызываетсяsecond()→ создаётся FEC‑second, «надстраивается» надfirst. secondвызываетthird()→ появляется FEC‑third.thirdзавершает работу, её контекст удаляется, стек возвращается кsecond.- После вывода
second endконтекстsecondудаляется, стек обратно кfirst. - Выводятся
first end, затем удаляетсяfirst, оставшийся только GEC.
Таким образом, стек гарантирует, что каждая функция завершится полностью, прежде чем управление вернётся к её вызывающему коду.
Внутреннее исполнение функций
При вызове функции движок создаёт активный объект (Activation Object), который объединяет:
- Переменные и параметры функции.
- Внутренние ссылки (
arguments), позволяющие обращаться к переданным аргументам. - Ссылку на внешний контекст через
[[Scope]], обеспечивая работу замыканий.
После создания активного объекта происходит та же двухфазная обработка: в фазе создания переменным присваивается undefined, а функции – полностью. Затем, в фазе исполнения, значения параметров заменяются реальными аргументами, а тело функции выполняется построчно. По завершении контекст удаляется из стека, а все ссылки, не удерживаемые замыканиями, освобождаются сборщиком мусора.
Эта модель обеспечивает предсказуемое поведение кода, упрощает отладку и позволяет реализовывать такие возможности JavaScript, как асинхронные колбэки, промисы и генераторы, без изменения базовой структуры контекстов и стека вызовов.