Reference и Lexical Environment
В спецификации ECMAScript идентификатор не хранит значение — он хранится как Reference Record, а резолвится через цепочку Lexical Environment → Environment 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