Я пытался как можно глубже понять, как работает javascript под капотом, и одновременно пытался изучить спецификацию. Меня немного смущает описание ForLoopEvaluation
Согласно спецификации:
ForStatement : for ( LexicalDeclaration Expressionopt; Expressionopt ) Оператор
Пусть oldEnv будет
running execution context'sLexicalEnvironment.Пусть loopEnv будет NewDeclarativeEnvironment(oldEnv).
Пусть isConst будет IsConstantDeclaration of LexicalDeclaration.
ПустьboundNames будет BoundNames из LexicalDeclaration.
Для каждого элемента dn изboundNames выполните
а. Если isConst истинно, то я. Выполнять ! loopEnv.CreateImmutableBinding(dn, true).
б. Еще, я. Выполнять ! loopEnv.CreateMutableBinding(dn, false).
Установите для LexicalEnvironment текущего контекста выполнения значение loopEnv.
Пусть forDcl будет завершением (вычислением лексического объявления).
Если forDcl является внезапным завершением, то
а. Задайте для LexicalEnvironment текущего контекста выполнения значение oldEnv.
б. Возвращаться ? для Dcl.
Если isConst ложно, пусть perIterationLets будетboundNames; в противном случае пусть perIterationLets будет новым пустым списком.
Если присутствует первое Выражение, пусть test будет первым Выражением; в противном случае пусть test будет пустым.
Если присутствует второе Выражение, пусть приращение будет вторым Выражением; в противном случае пусть приращение будет пустым.
Пусть bodyResult будет Completion(ForBodyEvaluation(test, increment, Statement, perIterationLets, labelSet)).
Задайте для LexicalEnvironment текущего контекста выполнения значение oldEnv.
Возвращаться ? телоРезультат.
Теперь, если мы посмотрим на ForBodyEvaluation
14.7.4.3 ForBodyEvaluation (test, increment, stmt, perIterationBindings, labelSet)
Абстрактная операция ForBodyEvaluation принимает аргументы test (узел синтаксического анализа выражения или пустой), increment (узел синтаксического анализа выражения или пустой), stmt (узел синтаксического анализа оператора), perIterationBindings (список строк) и labelSet (список строк). и возвращает либо нормальное завершение, содержащее значение языка ECMAScript, либо резкое завершение. При вызове он выполняет следующие действия:
Пусть V не определено.
Выполнять ? CreatePerIterationEnvironment(perIterationBindings).
Повторить,
а. Если тест не пуст, то
- Пусть testRef будет ? Оценка теста.
- Пусть testValue будет ? ПолучитьЗначение(тестСсылка).
- Если ToBoolean(testValue) ложно, вернуть V.
б. Пусть результатом будет завершение (оценка stmt).
в. Если LoopContinues(result, labelSet) ложно, вернуть ? UpdateEmpty(результат, V).
д. Если result.[[Value]] не пусто, установите для V значение result.[[Value]].
е. Выполнять ? CreatePerIterationEnvironment(perIterationBindings).
ф. Если приращение не пусто, то я. Пусть incRef будет ? Оценка приращения. II. Выполнять ? ПолучитьЗначение(incRef).
Теперь, когда мы смотрим на CreatePerIterationEnvironment, он говорит следующее:
14.7.4.4 CreatePerIterationEnvironment ( perIterationBindings )
Абстрактная операция CreatePerIterationEnvironment принимает аргумент perIterationBindings (список строк) и возвращает либо обычное завершение, содержащее неиспользуемые, либо завершение броска. При вызове он выполняет следующие действия:
Если у perIterationBindings есть какие-либо элементы, то
а. Пусть lastIterationEnv будет LexicalEnvironment текущего контекста выполнения.
б. Пусть внешним будет lastIterationEnv.[[OuterEnv]].
в. Утверждение: внешний не равен нулю.
д. Пусть thisIterationEnv будет NewDeclarativeEnvironment(outer).
е. Для каждого элемента bn perIterationBindings выполните
- Выполнять ! thisIterationEnv.CreateMutableBinding(bn, false).
- Пусть lastValue будет ? lastIterationEnv.GetBindingValue(bn, true).
- Выполнять ! thisIterationEnv.InitializeBinding(bn, lastValue).
ф. Задайте для LexicalEnvironment текущего контекста выполнения значение thisIterationEnv.
Возврат неиспользованного.
Я не понимаю, когда выполняется цикл for, поскольку функция не вызывается, новый контекст выполнения не создается. Почему здесь для обычного цикла for говорится, что если есть какие-либо perIterationBindings, то lastIterationEnvironment должен быть LexicalEnvironnment текущего контекста выполнения?
Я могу ошибаться, но связано ли это с возможным использованием оператора запятой, при котором используется функция получения? Это единственное объяснение, которое я могу придумать, оправдывающее использование термина контекст выполнения в контексте традиционного цикла for.
Вы пробовали копаться в реализациях JS? V8, SpiderMonkey, JCore, Hermes и т. д., если вы знаете С++, я предлагаю вам заглянуть туда, так как их работа - следовать спецификации до T
Всегда есть работающий контекст выполнения, даже если функции нет. Ведь глобальный код тоже нужно как-то исполнять.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Это не имеет ничего общего с вызовами функций. Цикл for с лексическими привязками (let, const) создает новую область для каждой итерации — см. Объяснение `let` и область видимости блока с циклами for для получения более подробной информации.
В спецификации лексическая область видимости достигается путем создания вложенных записей декларативной среды. Самый внутренний из них, который активен в определенный момент во время выполнения, всегда сохраняется в LexicalEnvironment текущего контекста выполнения, он просто изменяется всякий раз, когда область действия входит или выходит из нее. Неважно, является ли контекст выполнения глобальным, созданным из вызова функции или чем-то другим.
Что касается lastIterationEnv, обратите внимание, что в первой итерации цикла (которая включает в себя первую оценку выражения test) lastIterationEnv — это loopEnv, которое было установлено в ForLoopEvaluation и в котором оператор LexicalDeclaration был оценен для инициализации переменных цикла. В последующих итерациях (если они есть) lastIterationEnv будет PerIterationEnvironment предыдущей итерации.
Я всегда рассматривал контекст выполнения как кадр стека вызовов, и когда выполняется цикл for, в стек вызовов не добавляется кадр. Правильно ли я говорю, что цикл for создает контекст выполнения, но не добавляется в стек вызовов?
Да, контекст выполнения — это кадр стека вызовов. Но нет, цикл for не создает новый фрейм и не добавляет его в стек вызовов, он просто изменяет фрейм, который находится на вершине стека (обратите внимание, что стек никогда не бывает пустым при выполнении кода, всегда есть как минимум глобальный контекст). Он создает только новые записи среды (области действия).
Оператор for в строке 1 говорит: «Пусть oldEnv будет LexicalEnvironment текущего контекста выполнения», в этом случае он должен ссылаться на глобальный контекст выполнения. Это была часть, в которой я был сбит с толку, хотя цикл for не создает новый контекст выполнения, всегда есть глобальный контекст выполнения, и это часть, которая изменяется.
Что ж, если цикл for находится внутри функции, текущий контекст выполнения во время оценки цикла — это контекст вызова функции, а не обязательно глобальный.
Это все в спецификации - tc39.es/ecma262/#sec-returnifabrupt-shorthands к сожалению до сих пор не понятно!