Контекст выполнения и область видимости
Что такое Execution Context?
Контекст выполнения (EC) — окружение, в котором исполняется JavaScript-код: текущее значение this, переменные, объекты и функции, доступные коду.
Call Stack (стек вызовов)
Стек — структура данных LIFO (last in, first out). Стек выполнения JavaScript отслеживает все контексты выполнения, созданные за время жизни скрипта: при вызове функции её контекст кладётся на стек, при завершении — снимается.
Фазы создания и выполнения
Контекст выполнения создаётся в две фазы:
Creation Phase (фаза создания): движок анализирует код функции, но не выполняет его. Создаются два компонента:
- Lexical Environment
- Variable Environment
Execution Phase (фаза выполнения): движок проходит по коду ещё раз, присваивает значения переменным и выполняет код (инициализация привязок, присваивания, вызовы функций и т.д.).
Что такое this?
this — специальный объект, который динамически и неявно передаётся в код контекста выполнения. Это как неявный дополнительный параметр, к которому есть доступ, но который нельзя изменить напрямую.
Основное назначение
this— исполнять один и тот же код в контексте разных объектов.
Лексическое окружение (Lexical Environment)
Структура, связывающая идентификаторы контекста с их значениями; может иметь ссылку на родительское окружение. По сути — хранилище переменных, функций и классов области видимости данного контекста.
Объект лексического окружения состоит из двух частей:
- Environment Record — объект, где как свойства хранятся локальные переменные (и другая информация, например значение
this). - Ссылка на внешнее лексическое окружение — то, что соответствует коду снаружи текущих фигурных скобок.
Свободная переменная (Free variable)
Переменная, используемая функцией, но не являющаяся ни её параметром, ни локальной переменной.
function testFn() {
var localVar = 10;
function innerFn(innerParam) {
return innerParam + localVar; // localVar — свободная переменная
}
return innerFn;
}
testFn()(20); // 30Фунарг-проблема
Нисходящая (downward): неоднозначность при выборе лексического окружения свободной переменной — окружение времени создания или времени вызова?
let x = 10;
function foo() { console.log(x); }
function bar(funArg) {
let x = 20;
funArg(); // 10, а не 20!
}
bar(foo);Решается соглашением о статической области видимости (static scope) — используется окружение времени создания.
Восходящая (upward): захваченное окружение переживает породивший его контекст.
function foo() {
let x = 10;
return function bar() { return x; }; // восходящий фунарг
}
let x = 20;
foo()(); // 10, а не 20!См. также
- JS Basics — замыкания и прототипы
- Event Loop — call stack в рантайме