Контекст выполнения и область видимости

Что такое Execution Context?

Контекст выполнения (EC) — окружение, в котором исполняется JavaScript-код: текущее значение this, переменные, объекты и функции, доступные коду.


Call Stack (стек вызовов)

Стек — структура данных LIFO (last in, first out). Стек выполнения JavaScript отслеживает все контексты выполнения, созданные за время жизни скрипта: при вызове функции её контекст кладётся на стек, при завершении — снимается.


Фазы создания и выполнения

Контекст выполнения создаётся в две фазы:

Creation Phase (фаза создания): движок анализирует код функции, но не выполняет его. Создаются два компонента:

  1. Lexical Environment
  2. 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 в рантайме