我正在阅读规范(第12版)中使用类声明或表达式时发生的步骤,并看到它从第100 Runtime Semantics: ClassDefinitionEvaluation节创建类范围开始:

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

然后我可以看到规范中的这个算法创建并设置classBinding作为classScope环境记录的绑定(在步骤3a19a),但除此之外,我似乎看不到它还有什么用途.如果是这样的话,似乎只有类表达式(类名只能在类中使用,而不能在其声明的范围内使用)才需要创建classScope,而不是类声明.当类名绑定被添加到周围的作用域中时,为什么还要为类声明创建classScope(15.7.8为类声明运行上述算法15.7.7),这可能与类使用extends有关?

推荐答案

即使撇开在该规范的里程碑副本发布(12)后添加的现代功能不谈,类声明仍然需要类作用域来正确处理类内的类绑定.

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

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

虽然这是真的,但类作用域中的内部绑定仍然是创建的,这对于正确解析其中的类绑定非常重要.类中的构造函数和方法不必依赖于该外部绑定进行类绑定,尤其是因为该外部绑定为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 fieldsmethods进一步使用了类作用域,但在添加它们之前就已经需要了.

一个棘手的问题是你在 comments 我之前的错误答案时指出的顺序:

  • 5.a.将正在运行的执行上下文的词典环境设置为classScope.
  • 5.b.让superclassRef成为判断ClassHeritage的结果.
  • 5.c.将正在运行的执行上下文的LexicalEnvironment设置为env.

为什么要将正在运行的执行上下文的词典环境设置为classScope以计算ClassHeritage

Bergi给出了这个问题的答案,这个答案和上面method的答案是一样的:所以类的绑定在ClassHeritage内得到了正确的解决.他非常简洁的例子使用了一个尚未在该规范中出现的方案(静态方法),但它仍然说明了一点:

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

这显示了类X的一个类表达式,它扩展了类Y,其中类Y是在ClassHeritage语法产品中定义的,并且引用了X!(这是个好主意吗?Probably not.但可能会有非常尖锐的边缘 case .)

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

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

所以我们就在这里,即使在添加类字段等之前,类声明也可以使用classScope.

Javascript相关问答推荐

如何在表格上拥有水平滚动条,在正文页面上拥有垂直滚动条,同时还对html表格的标题使用位置粘性?

在贝塞尔曲线的直线上找不到交叉点:(使用@Pomax的bezier.js)

我应该在redux reducer中调用其他reducer函数吗?

判断表格单元格中是否存在文本框

Snowflake JavaScript存储过程返回成功,尽管预期失败

Google maps API通过API返回ZERO_RESULTS,以获得方向请求,但适用于Google maps

CheckBox作为Vue3中的一个组件

编剧如何获得一个div内的所有链接,然后判断每个链接是否都会得到200?

JS:XML insertBefore插入元素

闭包是将值复制到内存的另一个位置吗?

Vaadin定制组件-保持对javascrip变量的访问

在Odoo中如何以编程方式在POS中添加产品

如何在独立的Angular 应用程序中添加Lucide-Angel?

我在哪里添加过滤器值到这个函数?

Plotly.js栏为x轴栏添加辅助文本

将以前缓存的 Select 器与querySelector()一起使用

如何在不将整个文件加载到内存的情况下,在Node.js中实现Unix粘贴命令?

我想为我的Reaction项目在画布上加载图像/视频,图像正在工作,但视频没有

将匿名函数附加到链接的onClick属性

使用重新 Select 和对象理解 Select 器备忘