在EcmaScript规范中的For循环

huangapple go评论60阅读模式
英文:

For Loops in the EcmaScript specification

问题

根据您提供的内容,以下是翻译好的部分:

根据规范:

  • ForStatement:for ( LexicalDeclaration Expression<sub>opt</sub>; Expression<sub>opt</sub> ) Statement
    1. oldEnv 成为“运行执行上下文”的 LexicalEnvironment。
    2. loopEnv 成为 NewDeclarativeEnvironment(oldEnv)。
    3. 让 isConst 成为 LexicalDeclaration 的 IsConstantDeclaration。
    4. 让 boundNames 成为 LexicalDeclaration 的 BoundNames。
    5. 对于绑定名 boundNames 中的每个元素 dn,执行
      a. 如果 isConst 为 true,则执行 ! loopEnv.CreateImmutableBinding(dn, true)。
      b. 否则,
      i. 执行 ! loopEnv.CreateMutableBinding(dn, false)。
    6. 设置运行执行上下文的 LexicalEnvironment 为 loopEnv。
    7. 让 forDcl 成为 LexicalDeclaration 的评估的 Completion。
    8. 如果 forDcl 是突然的完成,则
      a. 设置运行执行上下文的 LexicalEnvironment 为 oldEnv。
      b. 返回 ? forDcl。
    9. 如果 isConst 为 false,则让 perIterationLets 成为 boundNames;否则让 perIterationLets 成为一个新的空列表。
    10. 如果第一个 Expression 存在,则让 test 成为第一个 Expression;否则,让 test 为空。
    11. 如果第二个 Expression 存在,则让 increment 成为第二个 Expression;否则,让 increment 为空。
    12. 让 bodyResult 成为 Completion(ForBodyEvaluation(test, increment, Statement, perIterationLets, labelSet))。
    13. 设置运行执行上下文的 LexicalEnvironment 为 oldEnv。
    14. 返回 ? bodyResult。

接下来,对于 ForBodyEvaluation 的描述:

  • 14.7.4.3 ForBodyEvaluation ( test, increment, stmt, perIterationBindings, labelSet )
    • 抽象操作 ForBodyEvaluation 接受参数 test(一个 Expression 解析节点或空)、increment(一个 Expression 解析节点或空)、stmt(一个 Statement 解析节点)、perIterationBindings(一个字符串列表)和 labelSet(一个字符串列表),并返回包含 ECMAScript 语言值的正常完成或突然完成。在调用时执行以下步骤:
      1. 让 V 为 undefined。
      2. 执行 ? CreatePerIterationEnvironment(perIterationBindings)。
      3. 重复,
        a. 如果 test 不为空,则

        • 让 testRef 成为 ? test 的评估。
        • 让 testValue 成为 ? GetValue(testRef)。
        • 如果 ToBoolean(testValue) 为 false,则返回 V。
          b. 让 result 成为 Completion(stmt 的评估)。
          c. 如果 LoopContinues(result, labelSet) 为 false,则返回 ? UpdateEmpty(result, V)。
          d. 如果 result.[[Value]] 不为空,则将 V 设置为 result.[[Value]]。
          e. 执行 ? CreatePerIterationEnvironment(perIterationBindings)。
          f. 如果 increment 不为空,则
          i. 让 incRef 成为 ? increment 的评估。
          ii. 执行 ? GetValue(incRef)。

最后,关于 CreatePerIterationEnvironment 的描述:

  • 14.7.4.4 CreatePerIterationEnvironment ( perIterationBindings )
    • 抽象操作 CreatePerIterationEnvironment 接受参数 perIterationBindings(一个字符串列表),并返回包含未使用的正常完成或抛出完成。在调用时执行以下步骤:
      1. 如果 perIterationBindings 有任何元素,则
        a. 让 lastIterationEnv 成为运行执行上下文的 LexicalEnvironment。
        b. 让 outer 成为 lastIterationEnv.[[OuterEnv]]。
        c. 断言:outer 不为 null。
        d. 让 thisIterationEnv 成为 NewDeclarativeEnvironment(outer)。
        e. 对于 perIterationBindings 中的每个元素 bn,执行

        • 执行 ! thisIterationEnv.CreateMutableBinding(bn, false)。
        • 让 lastValue 成为 ? lastIterationEnv.GetBindingValue(bn, true)。
        • 执行 ! thisIterationEnv.InitializeBinding(bn, lastValue)。
          f. 设置运行执行上下文的 LexicalEnvironment 为 thisIterationEnv。
      2. 返回 unused。

关于您的疑问,通常情况下,在执行常规的 for 循环时,确实不会创建新的执行上下文。但是,对于 for 循环中的 perIterationBindings,它们是用于跟踪迭代过程中的绑定变量的。创建 perIterationBindings 环境的目的是为了确保迭代过程中的变量不会干扰外部作用域的变量,以便实现正确的循环语义。

所以,为了保持 perIterationBindings 的隔离性,规范中使用了 "running execution context's LexicalEnvironment" 来确保每次迭代都在一个新的环境中运行,以便正确处理变量。这确保了在 for 循环的每次迭代中都有一个独立的环境来存储迭代过程中的变量。

这并不涉及到函数调用或逗号运算符,而是确保循环的每个迭代都有自己的环境,以防止变量之间的干扰。这是为了满足 JavaScript 语言规范中对 for 循环语义的要求。

英文:

I have been trying to understand how javascript works under the hood as deep as possible and have been trying to learn the spec simultaneously. I am a bit confused at the description of ForLoopEvaluation

According to the spec:

> ### ForStatement : for ( LexicalDeclaration Expression<sub>opt</sub>; Expression<sub>opt</sub> ) Statement
> 1. Let oldEnv be the running execution context&#39;s LexicalEnvironment.
> 2. Let loopEnv be NewDeclarativeEnvironment(oldEnv).
> 3. Let isConst be IsConstantDeclaration of LexicalDeclaration.
> 4. Let boundNames be the BoundNames of LexicalDeclaration.
> 5. For each element dn of boundNames, do
>
> a. If isConst is true, then
> i. Perform ! loopEnv.CreateImmutableBinding(dn, true).
>
> b. Else,
> i. Perform ! loopEnv.CreateMutableBinding(dn, false).
> 6. Set the running execution context's LexicalEnvironment to loopEnv.
> 7. Let forDcl be Completion(Evaluation of LexicalDeclaration).
> 8. If forDcl is an abrupt completion, then
>
> a. Set the running execution context's LexicalEnvironment to oldEnv.
>
> b. Return ? forDcl.
> 9. If isConst is false, let perIterationLets be boundNames; otherwise let perIterationLets be a new empty List.
> 10. If the first Expression is present, let test be the first Expression; otherwise, let test be empty.
> 11. If the second Expression is present, let increment be the second Expression; otherwise, let increment be empty.
> 12. Let bodyResult be Completion(ForBodyEvaluation(test, increment, Statement, perIterationLets, labelSet)).
> 13. Set the running execution context's LexicalEnvironment to oldEnv.
> 14. Return ? bodyResult.

Now if we take a look at the ForBodyEvaluation

> #### 14.7.4.3 ForBodyEvaluation ( test, increment, stmt, perIterationBindings, labelSet )
>
>The abstract operation ForBodyEvaluation takes arguments test (an Expression Parse Node or empty), increment (an Expression Parse Node or empty), stmt (a Statement Parse Node), perIterationBindings (a List of Strings), and labelSet (a List of Strings) and returns either a normal completion containing an ECMAScript language value or an abrupt completion. It performs the following steps when called:
>
> 1. Let V be undefined.
> 2. Perform ? CreatePerIterationEnvironment(perIterationBindings).
> 3. Repeat,
>
> a. If test is not empty, then
>
> - Let testRef be ? Evaluation of test.
> - Let testValue be ? GetValue(testRef).
> - If ToBoolean(testValue) is false, return V.
>
> b. Let result be Completion(Evaluation of stmt).
>
> c. If LoopContinues(result, labelSet) is false, return ? UpdateEmpty(result, V).
>
> d. If result.[[Value]] is not empty, set V to result.[[Value]].
>
> e. Perform ? CreatePerIterationEnvironment(perIterationBindings).
>
> f. If increment is not empty, then
> i. Let incRef be ? Evaluation of increment.
> ii. Perform ? GetValue(incRef).

Now when we look at CreatePerIterationEnvironment, it has this to say:

> #### 14.7.4.4 CreatePerIterationEnvironment ( perIterationBindings )
>
>The abstract operation CreatePerIterationEnvironment takes argument perIterationBindings (a List of Strings) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:
>
> 1. If perIterationBindings has any elements, then
>
> a. Let lastIterationEnv be the running execution context's LexicalEnvironment.
>
> b. Let outer be lastIterationEnv.[[OuterEnv]].
>
> c. Assert: outer is not null.
>
> d. Let thisIterationEnv be NewDeclarativeEnvironment(outer).
>
> e. For each element bn of perIterationBindings, do
>
> - Perform ! thisIterationEnv.CreateMutableBinding(bn, false).
> - Let lastValue be ? lastIterationEnv.GetBindingValue(bn, true).
> - Perform ! thisIterationEnv.InitializeBinding(bn, lastValue).
>
> f. Set the running execution context's LexicalEnvironment to thisIterationEnv.
>
> 2. Return unused.

I do not understand when a for loop is executed, since no function is being called a new execution context is not created. Why is it saying here for a normal for loop that if there are any perIterationBindings, that the lastIterationEnvironment should be the running execution context's LexicalEnvironnment?

I could be wrong but is this due to a possible use of the comma operator whereby a getter function is used? This is the only explanation I can come up with that would warrant the use of the term execution context being used in the context of a traditional for loop.

答案1

得分: 1

这与函数调用无关。具有词法绑定(letconst)的for循环为每次迭代创建新的作用域 - 有关更多详细信息,请参阅 https://stackoverflow.com/q/30899612/1048572

在规范中,通过创建嵌套的声明性环境记录来实现词法作用域。在执行过程中,在特定点处活动的最内层环境记录始终存储在运行执行上下文的LexicalEnvironment中,只要进入或离开作用域,它就会被简单地更改。无论执行上下文是全局的,是从函数调用创建的,还是其他情况,都没有关系。

关于lastIterationEnv,请注意,在循环的第一次迭代中(包括对test表达式的第一次评估),lastIterationEnv是在ForLoopEvaluation中设置的loopEnv,在其中执行了LexicalDeclaration语句以初始化循环变量。在后续的迭代中(如果有的话),lastIterationEnv将是上一次迭代的PerIterationEnvironment

英文:

This has nothing to do with function calls. A for loop with lexical bindings (let, const) is creating a new scope for every iteration - see https://stackoverflow.com/q/30899612/1048572 for more details.

In the spec, lexical scoping is achieved by creating nested Declarative Environment Records. The innermost one that is active at a particular point during execution is always stored in the running execution context's LexicalEnvironment, it is simply changed whenever a scope is entered or left. It does not matter whether the execution context is the global one, was created from a function call, or something else.

Regarding lastIterationEnv, notice that in the first iteration of the loop (which includes the first evaluation of the test expression), the lastIterationEnv is the loopEnv that was set up in ForLoopEvaluation and in which the LexicalDeclaration statement was evaluated to initialise the loop variables. In the subsequent iterations (if there are any), the lastIterationEnv will be the PerIterationEnvironment of the previous iteration.

huangapple
  • 本文由 发表于 2023年6月9日 02:00:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76434547.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定