По крайней мере так сказал блогер Demimurych у меня в комментариях:

Дальше была беседа в комментариях, из который я вынес два хороших замечания:
Тему замыканий в языках программирования стоит рассказывать с проблемы функционального аргумента.
Реализация этой проблемы наиболее и единственно полно описана в ECMAScript спецификации.
Я потратил время и покажу, что нашёл!
Несколько разборов примеров выложил на степике и сама статья есть на ютубе.
Почему все говорят о замыканиях?
Чтобы понять, зачем этот механизм вообще нужен, нам нужно вернуться к основам проектирования языков. Есть такая проблема, которую называют проблемой функционального аргумента.
У нас есть функция, которая использует переменную не из своих аргументов, а 'снаружи'. Мы передаем эту функцию в другое место и вызываем.
Вопрос: где она должна искать эту внешнюю переменную?
Есть два пути:
Нисходящий ФунАрг: Искать переменную там, где функция была вызвана. То есть смотреть вниз по стеку вызовов. Так работает, например, Bash.
Восходящий ФунАрг: Искать переменную там, где функция была объявлена в коде. То есть смотреть вверх по тексту программы. И это — выбор JavaScript.
Давайте почувствуем разницу!
Код на языке Bash
#!/bin/bash
x=10
printX() {
echo "$x"
}
run() {
local x=20
printX
}
run
Код на языке JavaScript
var x = 10;
function printX() {
console.log(x);
}
function run() {
var x = 20;
printX();
}
run();
Выведет: 10
.
Народные мифы или ECMAScript
Миф |
ECMAScript |
Замыкание — это функция, возвращающая функцию. |
Замыкание создаётся при создании любой функции в момент её определения, а не возврата. |
Замыкание нужно, чтобы 'замкнуть' (сохранить) переменную. |
Механизм позволяет функции иметь доступ к целой цепочке записей окружения, а не к одной переменной |
Замыкание — это особый объект с переменными |
Это не объект, а внутреннее поле функции — ссылка на её 'родную' запись окружения |
Демонстрируется только на примере с возвратом функции |
Демонстрируется на любом обращении к идентификатору (переменной) вне функции |
Хватит пустых слов, вперёд к академическим определениям!
Спецификация
Словарик
Я не люблю англицизмы, но не люблю и путанницу! Поэтому привожу определения из документации и их переводы.
BlockStatement = любой блок кода в {...}
Call Stack = Execution Context Stack = стек вызовов = стек контекстов исполнения
Closure = замыкание
Declarative Environment Record = декларативные записи окружения
ECMAScript = документация = спецификация
Environment Record = запись окружения = окружение
Execution Context = контекст выполнения
Function Environment Records = записи окружения функции
FunctionDeclaration = декларативное объявление функции
Global Environment Record = глобальные записи окружений
LexicalEnvironment = лексическое окружение
Module Environment Records = записи окружения модулей
OuterEnv = внешнее (родительское) окружение
VariableEnvironment = окружение переменных
Определение
Замыкание — это концептуальная пара, состоящая из функции и ссылки на её внешнее окружение, в котором эта функция была создана.
С точки зрения спецификации ECMA-262, это означает, что каждый объект функции при своём создании получает некое внутреннее свойство. Это свойство содержит ссылку на Environment Recodr того контекста выполнения, в котором функция была объявлена (скоро покажу всё на пальцах).
Благодаря этой ссылке, функция "помнит" свое "родное" окружение и может обращаться к переменным и функциям из этого окружения, даже если она вызывается в совершенно другом месте программы, где этих переменных уже нет в текущем окружении.
Механизм разрешения идентификаторов рекурсивно следует по цепочке внешних окружений, начиная с самой функции. Именно эта цепочка и составляет то, что мы на практике называем областью видимости.
Если обратиться к спецификации 1998 года, то там использовалась более простая модель с "Цепочкой областей видимости" (Scope Chain), которая была списком объектов. При создании функции она "захватывала" эту цепочку. Суть осталась той же, но современная модель с Environment Records более точна и позволяет элегантно описать блочную область видимости (let
, const
).
Таким образом, замыкание — это не какая-то особая сущность, а фундаментальный механизм работы записей окружения в JavaScript, реализованный через сохранение функцией ссылки на свою запись окружения в момент создания.
Изучая спецификацию
9.1 Environment Records
Environment Record is a specification type used to define the association of Identifiers to specific variables and functions, based upon the lexical nesting structure of ECMAScript code. Usually an Environment Record is associated with some specific syntactic structure of ECMAScript code such as a FunctionDeclaration, a BlockStatement, or a Catch clause of a TryStatement. Each time such code is evaluated, a new Environment Record is created to record the identifier bindings that are created by that code.
Every Environment Record has an [[OuterEnv]] field, which is either null or a reference to an outer Environment Record. This is used to model the logical nesting of Environment Record values. The outer reference of an (inner) Environment Record is a reference to the Environment Record that logically surrounds the inner Environment Record. An outer Environment Record may, of course, have its own outer Environment Record. An Environment Record may serve as the outer environment for multiple inner Environment Records. For example, if a FunctionDeclaration contains two nested FunctionDeclarations then the Environment Records of each of the nested functions will have as their outer Environment Record the Environment Record of the current evaluation of the surrounding function.
Environment Records are purely specification mechanisms and need not correspond to any specific artefact of an ECMAScript implementation. It is impossible for an ECMAScript program to directly access or manipulate such values.
Глава 9.1 “Environment Records” определяет понятие “Environment Record” (Запись окружения) как внутренний механизм для управления переменными, функциями и другими идентификаторами.
Каждый раз, когда движок JS встречает определенную синтаксическую конструкцию, создаётся новая Запись Окружения (условно: список переменных). К таким конструкциям относятся:
FunctionDeclaration (Декларативное объявление функции)
BlockStatement (Любой блок кода в
{...}
)
Ключевой элемент этой модели — поле [[OuterEnv]]
. Каждая Запись Окружения содержит ссылку на внешнее (родительское) окружение. Это создает связанный список или цепочку окружений, которая тянется от самого вложенного кода до глобального уровня. Именно эта цепочка и поиск идентификатора по ней и является так называемым "замыканием".
Важно понимать, что Записи Окружения — это абстракция спецификации. Мы не можем получить к ним доступ из нашего JavaScript-кода или увидеть их в DevTools.
И вообще, в DevTools показывают лишь абстрактные понятия, которые могут не совпадать с именами из ECMAScript.
9.1.1 The Environment Record Type Hierarchy
Environment Records can be thought of as existing in a simple object-oriented hierarchy where Environment Record is an abstract class with three concrete subclasses:
Declarative Environment Record,
Function Environment Records,
Module Environment Records,
Global Environment Record.
Each Declarative Environment Record is associated with an ECMAScript program scope containing
var
iable,const
ant,let
, class, module, import, and/or function declarations.
A Declarative Environment Record binds the set of identifiers defined by the declarations contained within its scope.
Глава 9.1.1 “The Environment Record Type Hierarchy” определяет иерархию типов записей окружения:
-
Запись окружения
-
Декларативные записи окружения
Записи окружения функций - создается при вызове функции.
Записи окружения модулей.
Глобальные записи окружений - глобальная область видимости. У нее
[[OuterEnv]]
равенnull
, так как выше ничего нет.
-
Каждая декларативная запись напрямую связывает идентификаторы (имена переменных) с их значениями в пределах своей области видимости.
9.1.2 Environment Record Operations
The following abstract operations are used in this specification to operate upon Environment Records:
9.1.2.1 GetIdentifierReference ( env, name, strict )
The abstract operation GetIdentifierReference takes arguments env (an Environment Record or null), name (a String), and strict (a Boolean) and returns either a normal completion containing a Reference Record or a throw completion. It performs the following steps when called:
If env is null, then
Return the Reference Record { unresolvable }.
Let exists be ? env.HasBinding(name).
If exists is true, then
Return the Reference Record { env }.
Else,
Let outer be env.[[OuterEnv]].
Return ? GetIdentifierReference(outer, name, strict).
Глава 9.1.2 “Environment Record Operations” определяет алгоритм поиска идентификатора (переменной):
Поискать идентификатор (переменную) в текущей Записи Окружения.
Если он найден, вернуть ссылку на него.
Если нет, взять ссылку на внешнее окружение из поля
[[OuterEnv]]
.Повторить поиск уже в этом внешнем окружении (рекурсивно).
Если мы дошли до самого верхнего уровня (где
[[OuterEnv]]
равенnull
) и ничего не нашли, значит, переменная не объявлена (возвращается unresolvable reference, что приведет кReferenceError
).
Этот простой обход по цепочке [[OuterEnv]]
и есть механизм, который позволяет коду внутри функции "видеть" переменные снаружи.
Table 26: Additional State Components for ECMAScript Code Execution Contexts
LexicalEnvironmentIdentifies the Environment Record used to resolve identifier references made by code within this execution context.VariableEnvironmentIdentifies the Environment Record that holds bindings created by VariableStatements within this execution context.
The LexicalEnvironment and VariableEnvironment components of an execution context are always Environment Records.
An execution context is purely a specification mechanism and need not correspond to any particular artefact of an ECMAScript implementation. It is impossible for ECMAScript code to directly access or observe an execution context.
Таблица 26 “Additional State Components for ECMAScript Code Execution Contexts” рассказывает, что каждый раз при выполнении функции создаётся контекст её выполнения. Это ещё одна абстракция, которая отслеживает состояние выполняемого кода. У каждого контекста есть как минимум два важных для нас компонента:
LexicalEnvironment (Лексическое окружение) — ссылка на Запись Окружения, используемую для разрешения имён. Именно здесь хранятся объявления
let
,const
,class
.VariableEnvironment (Окружение переменных) — ссылка на Запись Окружения для объявлений
var
иfunction
.
В чём разница между LexicalEnvironment и VariableEnvironment?
Это различие — ключ к пониманию разницы между var
и let
/const
.
VariableEnvironment создается для всей функции целиком и не меняется во время её выполнения. Именно сюда "всплывают" все объявления
var
(народное понятие "hoisting" работает именно из-за этого).LexicalEnvironment является более динамичным. Изначально он совпадает с VariableEnvironment. Но когда выполнение входит в новый блок (например,
if
или циклfor
), создается новая LexicalEnvironment для этого блока, а её[[OuterEnv]]
указывает на предыдущую.
Это и есть механизм блочной области видимости. Переменные let
и const
существуют только в LexicalEnvironment своего блока.
И да, контекст выполнения функции — это временная сущность. Он создается при вызове функции и уничтожается после её завершения (удаляется из стека вызовов).
Как и Записи Окружения, Контекст Выполнения — это абстракция спецификации. Мы не можем получить к нему доступ из нашего JavaScript-кода или увидеть его в DevTools.
9.4.2 ResolveBinding ( name [ , env ] )
The abstract operation ResolveBinding takes argument name (a String) and optional argument env (an Environment Record or undefined) and returns either a normal completion containing a Reference Record or a throw completion. It is used to determine the binding of name. env can be used to explicitly provide the Environment Record that is to be searched for the binding. It performs the following steps when called:
If env is not present or env is undefined, then
Set env to the running execution context's LexicalEnvironment.
Assert: env is an Environment Record.
Return ? GetIdentifierReference(env, name).
Глава 9.4.2 “ResolveBinding” определяет точку входа для поиска идентификатора (переменной).
Алгоритм поиска записи окружения:
Взять LexicalEnvironment из текущего выполняемого контекста.
Запустить для этого окружения алгоритм поиска идентификатора из таблицы 26 “Additional State Components for ECMAScript Code Execution Contexts”.
Вывод
Фундаментальная идея — сохранение ссылки на цепочку внешних записей окружения при создании функции — осталась неизменной. Современная модель с Environment Records просто является более точной и гибкой, что позволило ввести блочные записи окружения, не ломая старую логику.
Объёмный пример

function hi() {
function say() {
var name = "John";
let middleName = "J.";
{
let surname = "Doe";
console.log(`Hi, ${name} ${middleName} ${surname}`);
}
}
return say();
}
hi();
Рассмотрим работу контекста выполнения, стека вызовов и записей окружения с точки зрения JavaScript движка.
Функция global()
В JavaScript всё является (объек..) функцией! Начиная с самой первой строчки кода мы уже в глобальной функции. И она всегда есть в механизме Стека Вызовов.
Стек вызовов |
|
|
|
|
Раз мы внутри функции и мы исполняем её – нам (как движку JS) полагается механизм исполнения: Контекст выполнения. У него есть два компонента, можем рассматривать их как прокси / каналы / указатели на нужную запись окружения. Соответственно и для каждого контекста выполнения полагается запись окружения. Мы исполняем функцию, значит и будет запись окружения функции.
Контекст выполнения функции |
|
Лексическое окружение |
|
Окружение переменных |
|
Запись окружения функции |
|
|
объект функции hi |
|
|
Рассмотрели всё, последней строкой идёт вызов этой функции hi()
.
Функция hi()
Стек вызовов |
|
|
|
|
Всё то же самое, очередная функция, очередной контекст исполнения, очередная запись окружения.
Контекст выполнения функции |
|
Лексическое окружение |
|
Окружение переменных |
|
Запись окружения функции |
|
|
объект функции say |
|
Запись окружения функции global() |
Рассмотрели всё, последней строкой идёт вызов этой функции say().
Функция say()
Стек вызовов |
|
|
|
|
Всё то же самое, очередная функция, очередной контекст исполнения, очередная запись окружения.
Контекст выполнения функции |
|
Лексическое окружение |
Запись окружения функции say() |
Окружение переменных |
|
Запись окружения функции |
|
|
|
|
|
|
Запись окружения функции hi() |
Но! Тут важно увидеть, что при заходе в блочную конструкцию {...}
мы откроем новую декларативную запись окружения. Туда попадут все let
const
переменные. И пока мы будем внутри этого блока, лексическое окружение будет ссылаться на неё.
Контекст выполнения функции |
|
Лексическое окружение |
Декларативная запись окружения блока {...} |
Окружение переменных |
|
Декларативная запись окружения блока |
|
|
|
|
Запись окружения функции say() |
Рассмотрели всё, последней строкой идёт вывод переменных в консоль.
Несколько разборов примеров выложил на степике и сама статья есть на ютубе.
Комментарии (13)
domix32
02.10.2025 07:27Народные мифы или ECMAScript
такие себе мифы.
Замыкание создаётся при создании любой функции в момент её определения, а не возврата.
что в общем-то никак не противоречит утверждению, что это функция, вернувшая функцию. Ну и не любой функции, а функции содержащей то самое определение. если есть функция foo() которая никак не связывается с bar(), которая в свою очередь содержит замыкание, то при создании foo замыкание в bar не будет создаваться.
цепочке записей окружения,
что бы это не значило. обычно замыкания рассматривают как функцию и некоторый контекст. что попадает в контекст не сильно принципиально, но именно посредством него происходит применение переменных в замыкания.
Замыкание — это особый объект с переменными
> Это не объект,
В JS буквально почти всё объект. В том числе и функции. У не объекта не должно быть никаких полей или методов.
Демонстрируется на любом обращении к внешней переменной
тут стоило бы пояснить, что такое внутренняя/внешняя переменная.
Environment Record = запись окружения = окружение
нет, это не то же самое что и окружение. Окружение состоит из набора записей. И они вроеде как не могут быть вложенными, то есть под ключ записи нельзя положить какой-нибудь отдельный набор записей.
это не какая-то особая сущность, а фундаментальный механизм работы записей окружения
так записи окружения даже с блочной видимостью никогда никого не захватывают, так что странно что вы их зовёте замыканиями. Захват контекста в функции зависит от записей окружения, но никак не наоборот. Как вы читали спеку и получили все шиворот на выворот?
FunctionDeclaration (Декларативное объявление функции)
что значит "декларативное"? вы сделали тавтологию на ровном месте. Определение/объявление (англ. Declaration) функции оно и в африке им остаётся.
Именно эта цепочка и является реализацией замыкания.
учитывая, что вся цепочка видимости, как и различные окружения - абстракция спеки, о чем вам написали парой абзацев выше, почему вы зовёте это РЕАЛИЗАЦИЕЙ?
Она дополнительно управляет
this
.ничем она не управляет. this - это указатель объекта на себя и конструируется как часть объекта функции.
Этот простой рекурсивный обход
откуда дровишки про рекурсию? оно не вызывает само себя, оно просто линейно проходит по списку.
И дальше по статье ещё кучка таких вот отсебятин.
AlekseiVolkov Автор
02.10.2025 07:27Спасибо за критику. Исправил в статье все претензии по существу.
Остальное, ответы на комментарии:
что в общем-то никак не противоречит утверждению, что это функция, вернувшая функцию. Ну и не любой функции, а функции содержащей то самое определение. если есть функция foo() которая никак не связывается с bar(), которая в свою очередь содержит замыкание, то при создании foo замыкание в bar не будет создаваться.
без примера не могу понять связь этих функций. Я увидел, что по спеке JS не важно возвращается функция откуда-то или нет. "Замыкание", как механизм поиска идентификаторов (переменных внутри этой функции) есть всегда. Вернее сказать, что это особенность языка, выбор одного из решений проблемы фунарга.
что бы это не значило. обычно замыкания рассматривают как функцию и некоторый контекст. что попадает в контекст не сильно принципиально, но именно посредством него происходит применение переменных в замыкания.
Я увидел, что по спеке "замыкание" может быть и у блока, и у функции. И я увидел, что как раз порядок записей окружения и играет роль в поиске идентификатора.
В JS буквально почти всё объект. В том числе и функции. У не объекта не должно быть никаких полей или методов.
Функция – это объект, но "замыкание" - это механизм работы JS. Я это хотел подчеркнуть.
тут стоило бы пояснить, что такое внутренняя/внешняя переменная.
исправил ✅
нет, это не то же самое что и окружение. Окружение состоит из набора записей. И они вроеде как не могут быть вложенными, то есть под ключ записи нельзя положить какой-нибудь отдельный набор записей.
Я сократил название, чтобы было легче им оперировать. Да, окружения наследуются лишь через поле
[[OuterEnv]]
. "Environment Record is … used to define the association of Identifiers to… functions"Какое окружение имеете в виду вы?
так записи окружения даже с блочной видимостью никогда никого не захватывают, так что странно что вы их зовёте замыканиями. Захват контекста в функции зависит от записей окружения, но никак не наоборот. Как вы читали спеку и получили все шиворот на выворот?
Не понял комментария. Да, самого слова "замыкание" нет в спеке. Поэтому и глагол "захватывает" можно использовать условно.
Захват контекста в функции зависит от записей окружения
Это я тоже не понял. Что значит контекст? Он отличается от записей окружения?
что значит "декларативное"? вы сделали тавтологию на ровном месте. Определение/объявление (англ. Declaration) функции оно и в африке им остаётся.
Простое объявление функции идёт в запись окружения через компонент VariableEnvironment, а FunctionExpression созданное через
const
идёт через LexicalEnvironment.учитывая, что вся цепочка видимости, как и различные окружения - абстракция спеки, о чем вам написали парой абзацев выше, почему вы зовёте это РЕАЛИЗАЦИЕЙ?
исправил ✅
ничем она не управляет. this - это указатель объекта на себя и конструируется как часть объекта функции.
исправил ✅
откуда дровишки про рекурсию? оно не вызывает само себя, оно просто линейно проходит по списку.
исправил ✅.
Ещё раз спасибо. Я могу ошибаться.
domix32
02.10.2025 07:27без примера не могу понять связь этих функций.
function foo(args) { /* body */ } function bar(args) { return function(33, args..) { /* */} }
Вызов
foo
никак не повлияет на вызовfunction(33,args)
, то есть не любой вызов."Замыкание", как механизм поиска идентификаторов (переменных внутри этой функции) есть всегда.
это называется областью видимости, а не замыканием. замыкание это захват вызываемым объектом какого-то внешнего контекста - будь то глобальная область видимости, или уровень блока не сильно принципиально. Способов каким образом это делать также есть несколько и зависит от нюансов конкретного языка программирования.
В контексте спецификации оно и не должно существовать. Также как не существует таких вещей как функтор, типаж или монада или даже ООП с полиморфизмом и наследованием в той же спецификации. Это не значит, что их не может существовать в JS.
"замыкание" может быть и у блока
ещё раз - окружение и замыкание не одно и то же. каждый блок кода на стеке фактически имеет три постоянных ссылки - ссылку на глобальное окружение (GlobalEnv) , ссылку на окружение уровнем выше (OuterEnv) и ссылку на собственную область видимости (LexicalEnv). если на стек попадает какой-то новый объект, то ей LexicalEnv предыдущего блока попадает в OuterEnv текущего и создаётся новый LexicalEnv. Такая штука как VariableEnv на самом деле всегда существует в пределах GlobalEnv.
Кстати из-за непонимания вот этих вещей у вас код на баше и на js неравноценный, т.к. аналогом
local
в bash будет тот жеlet
в js, хотя общие принципы работы области видимости у них примерно совпадают.Да, окружения наследуются
наследование это маленько другое и работает через то что в спеке называется ObjectEnv, который содержит поля объектов класса/прототипа. OuterEnv это просто ссылка на LexicalEnv который был на стеке, либо GlobalEnv, если ничего другого не осталось.
Не понял комментария. Да, самого слова "замыкание" нет в спеке. Поэтому и глагол "захватывает" можно использовать условно.
На примере покажу
let a = 34; var x = 33; // // GlobalEnv { // vars : { # наш VariableEnv // x : 33 # наш объявленный var x // } // } // // LexicalEnv [[ Scope 1]] { # локальное окружение Scope1 // OuterEnv -> GlobalEnv # ссылка на внешнее окружение // x -> GlovalEnv.vars.x # ссылается на VariableEnv // a : 34 # // } // function foo() { // объявление foo попадает в GlobalEnv // новое состояние GlobalEnv // GlobalEnv { // vars : { # наш VariableEnv для переменных // x : 33 # наш объявленный var x // } // funcs: { # VariableEnv для function // foo : <...> # объявленная функция // } // } // конструируется локальное окружение // LexicalEnv [[ foo ]]{ # локальное окружение функции foo // this -> [[ foo ]] # ссылка на собственный LexicalEnv // OuterEnv -> [[ Scope1 ]] # ссылка на внешний контекст // const: [2] # локальные константы // a -> OuterEnv.a # ссылается на OuterEnv // x -> OuterEnv.x # по цепочке ссылается на VariableEnv в GlobalEnv //} return a + x + 2; // обращение будет выглядеть как // [[ foo ]].a + [[ foo ]].x + [[ foo ]].const[0] ==> // OuterEnv.a + OuterEnv.x + [[ foo ]].const[0] ==> // [[ Scope1 ]].a + GlobalEnv.vars.x + [[ foo ]].const[0] }
Вот и получается, что всё что не было в пределах LexicalEnv было захвачено в его контекст. Оно же "замкнуто". Но всё это естественно абстракции именно спецификации, в реальности оно вероятнее всего сделано несколько иначе для большей оптимальности.
Надеюсь моя нотация понятна?
->
обозначает что левое ссылается на правое, а:
соотвественно содержит значение,[[ asd ]]
просто обозначение сущности по имени для удобства.Это условная структура и не следует спеке досконально.
DmitryOlkhovoi
02.10.2025 07:27Вы малость тоже забываете откуда слово захват в JS, не нужно путать с другими языками где замыкание это буквально просто доступ к скоупу выше (за счет "захвата"), иначе у них нет других вариантов, когда функция видит, что-то выше.
В мире JS, замыкание, практически - это когда выйдя из скоупа, коллектор не рубит его из за использования. На собесах можно тролить, не все знают)) Но обычно когда речь про замыкание, вспоминают именно эту особенность.function outer() { let x = 10; // Захваченая переменная (captured variable) function inner() { console.log(x); // доступ к "верхней" переменной (closure) } inner() } outer(); // x
Переменная захвачена потому, что сборщик мусора не может освободить её окружение — на него указывает функция, которую мы сохранили.
То есть захват === продление жизни окружения за пределы времени работы внешней функции.
DmitryOlkhovoi
Лексическая область видимости хорошо описана в you don't know js и на сайте учебника https://learn.javascript.ru/
OuterEnv, не совсем понимаю, чтото новое походу. Это всегда был _scope
AlekseiVolkov Автор
1) Я попробовал написать статью с терминами спецификации: чтобы самому не путаться в "областях видимости", "скоупах", "блоках".
2) Я хотел максимально её визуализировать, чтобы самому что-то понять: вышло чёткое разделение Environmental Records и Lexical/Variable-Environment.
3) В документации learn.javascript.ru есть несколько "допущений", которые расходятся с EcmaScript и путают меня. Ведь V8 реализует EcmaScript, а не документацию learn.javascript.ru или блог MDN.
Примеры из документации learn.javascript.ru:
К сожалению, не нашёл слов depricated или прочих указаний, что это слово устарело =(
В начале статьи есть описание "блочной видимости", но после введения термина "LexicalEnvironment" такого примера уже нет. Остаётся неясным переход LexicalEnvironment от "объекта" функции к объекту блока.
4) Как и в пункте 1 я тоже путался. Что вы имеете в виду под _scope и OuterEnv?
[[OuterEnv]] я взял как поле, которое ссылается на родительскую запись окружения. 9.1 Environment Records
DmitryOlkhovoi
Короче я погуглил, раньше называлось [[Scope]]
[[Scope]]
(old) = whole chain of lexical environments stored with the function.[[OuterEnv]]
(new) = each environment points to its parent environment; a function just stores its immediate environment ([[Environment]]
), and the chain is followed through[[OuterEnv]]
.Но тут тоже надо отметить, что теперь это Environment, а в нем
OuterEnv
И самое главное, если любите рассказывать вещи, в статье нет за счет чего живет замыкание. Пару слов про сборщик мусора не помешало бы)) А то ссылки ссылками, лексический скоуп и так далее, все это интересно, но не объясняет джуну, почему мы после возврата из функции получаем где-то живущую переменную)) Кликбейт заголовок, а воды налито мимо)
AlekseiVolkov Автор
Круто! Спасибо за справку.
[[Environment]] - это поле у объекта функции, оно ссылается на запись окружения.
[[OuterEnv]] - это поле у записи окружения, оно ссылается на родительскую запись окружения.
Это как я понял.
Да, согласен, можно копать ещё дальше. Поделился тем, что самому было интересно. Для джунов не умею писать.
domix32
Scope оно и есть. Просто в контексте спецификации оно обозначается как окружение. OuterEnv - это просто ссылка на область видимости выше по стеку.
DmitryOlkhovoi
Раньше просто был [[Scope]] и ссылались через него. Так и в стандарте обзывали)
Видимо это уже сахар от девтулзов) надо обновлять знания))
AlekseiVolkov Автор
Так да!
Scope, closure, block - это в хром девтулзах, а полез в спеку - таких слов вообще нет.
Отсутствие документации по V8 - боль!