EcmaScript 规范中 classScope 的用途是什么?

What is the purpose of classScope in the EcmaScript specification?

我正在阅读规范(第 12 版)中使用 class 声明或表达式时发生的步骤,并看到它首先在 class 部分创建范围 15.7.7 运行时语义:ClassDefinitionEvaluation:

1. Let env be the LexicalEnvironment of the running execution context.
2. Let classScope be NewDeclarativeEnvironment(env).
...

然后我可以看到规范中的这个算法然后创建 classBinding 并将其设置为 classScope 环境记录的绑定(在步骤 3a,和 19a),但除此之外,我似乎看不出它还有什么用途。如果是这样的话,似乎只有 class 表达式才需要创建 classScope(其中 class 名称仅在 class 中可用,而在其声明的范围)而不是 class 声明。我很困惑为什么还为 class 声明创建 classScope15.7.8 对于 class 声明运行上述算法 15.7.7) 当 class 名称绑定添加到周围范围时,可能与 class 使用 extends?

时有关

即使搁置年度快照规范发布后添加的现代功能 (1, 2),class 声明仍然需要 class 范围来正确处理 class 绑定class.

您注意到 class 声明会在包含该声明的范围内为 class 名称创建绑定,而 class 表达式不会:

class Example1 { }
console.log(typeof Example1); // "function"
const x = class Example2 { };
console.log(typeof Example2); // "undefined"

尽管如此,class 范围内的内部绑定仍会创建,这对于在其中正确解析 class 绑定很重要。 class 中的构造函数和方法不必依赖 class 绑定的外部绑定,尤其是因为外部绑定是 mutable:

"use strict";
// Class declaration, creates binding in the current scope
class Example {
    method() {
        // Note this uses `Example`
        return new Example();
    }
}

// **BUT**, code can change that binding
const OldExample = Example;
Example = {};

const e1 = new OldExample();
// Should this fail because `Example` has been reassigned, but it's used by `method`?
const e2 = e1.method();
// No, it works just fine
console.log(e2 instanceof OldExample); // true

如果 method 依赖于 Example 的外部绑定,那么当它 new Example 时它会使用错误的东西。但是由于 class 范围,它不依赖于该绑定,它依赖于 class 范围内的内部绑定(这是不可变的)。

正如我提到的,class fields and methods 进一步使用 class 作用域,但在添加它们之前就需要它。

一个棘手的问题是您在对我之前的错误答案的评论中指出的这个序列:

  • 5.a. Set the running execution context's LexicalEnvironment to classScope.
  • 5.b. Let superclassRef be the result of evaluating ClassHeritage.
  • 5.c. Set the running execution context's LexicalEnvironment to env.

为什么将 运行 执行上下文的 LexicalEnvironment 设置为 classScope 只是为了评估 ClassHeritage

Bergi 给出了答案,它与上面 method 的答案相同:以便 class 的绑定在 [=72 中得到正确解析=]ClassHeritage。他的 wonderfully-succinct 示例使用了尚未纳入该规范的提案(静态方法),但它仍然说明了这一点:

// shows the class
(class X extends (class Y { static logX() { console.log(X); } }) { }).logX();

显示 class class X 的 class 表达式扩展 class Y,其中 class Y 是在 ClassHeritage 语法生成中定义 - 并引用 X! (这是个好主意吗?可能不是。但可能会有非常前卫的边缘情况。)

为清楚起见,让我们稍微扩展一下并坚持您链接到的规范中的功能:

const x = new class X extends (
    class Y {
        logX() {
            console.log(X);
        }
    }
) {
};
x.logX();               // shows the class
console.log(typeof X);  // undefined

因此,即使 class 声明也用于 classScope,甚至在添加 class 字段等之前。

class Example1 { }
console.log(typeof Example1); // "function"
const x = class Example2 { };
console.log(typeof Example2); // "undefined"