Reference и Lexical Environment

В спецификации ECMAScript идентификатор не хранит значение — он хранится как Reference Record, а резолвится через цепочку Lexical EnvironmentEnvironment Record.

Что это / Зачем

Понимание этих spec-понятий объясняет:

  • Почему все ошибки идентификаторов — ReferenceError
  • Как работает scope chain под капотом
  • Почему let создаёт hole, а var — undefined
  • Откуда берётся "lexical scope" (по тексту программы)

Reference Record

Спека-тип (не существует в runtime как объект). Состоит из:

  • Base — где искать значение (Environment Record или объект)
  • ReferencedName — имя идентификатора (строка или Symbol)
  • Strict flag — strict mode флаг
  • ThisValue — для super-property reference
// Когда вы пишете:
foo
// движок создаёт Reference Record: { Base: <Env>, Name: 'foo', Strict: true }

// Когда вы пишете:
foo = 5
// PutValue(ref, 5) — устанавливает в Base

Lexical Environment

Объект runtime, состоит из:

  • Environment Record — хранилище bindings (имя → значение/hole)
  • OuterEnv — ссылка на родительский Environment (для scope chain)
function outer() {
  const a = 1;
  function inner() {
    const b = 2;
    return a + b; // резолв: inner Env → outer Env → global
  }
  return inner;
}

При создании каждой функции/блока создаётся новый Lexical Environment. Замыкание = функция + захваченный outer environment.

Виды Environment Records

  • Declarative — для блоков, функций, catch (let, const, function, class)
  • Object — для глобального scope и with (binding = свойство объекта)
  • Function — особый Declarative с this, arguments, super
  • Module — для ES modules (import bindings)
  • Global — комбинация Declarative + Object (для var и function на верхнем уровне)

Все ошибки идентификаторов — ReferenceError

foo;                  // ReferenceError: foo is not defined
let x; console.log(z); // ReferenceError: z is not defined
{ console.log(x); let x; } // ReferenceError: Cannot access 'x' before initialization

Identifier — это всегда ссылка на запись в Environment Record. Если запись не найдена или hole → ReferenceError.

Подводные камни

  • var в global scope создаёт свойство global object (через Object Environment Record). let/const — нет
  • eval в strict mode создаёт отдельный Environment Record — переменные оттуда не утекают
  • with(obj) создаёт временный Object Environment Record поверх — отсюда непредсказуемость и запрет в strict

🎓 Источники

  • ⚡ [JavaScript and the Lexical Environment] · AsForJS · 2023-05-12 · YouTube
    • Тезисы: Lexical Environment = Environment Record + OuterEnv. Резолв идентификатора идёт по цепочке OuterEnv до global
  • ⚡ [JavaScript Беседы Идентификаторы] · AsForJS · 2023-10-21 · YouTube
    • Тезисы: identifier — это всегда Reference. Все ошибки = ReferenceError потому что идентификатор это ссылка
  • ⚡ [⎡spec 00⎦ JavaScript и Call Stack согласно спецификации] · AsForJS · 2023-11-03 · YouTube
    • Тезисы: Call Stack — это стек Execution Context. Каждый Execution Context содержит Lexical Environment и Variable Environment

См. также