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

Shn*_*ick 5 javascript class ecma262

我正在阅读规范(第 12 版)中使用类声明或表达式时发生的步骤,并发现它首先在第15.7.7节运行时语义:ClassDefinitionEvaluation中创建类作用域:

1. Let env be the LexicalEnvironment of the running execution context.
2. Let classScope be NewDeclarativeEnvironment(env).
...
Run Code Online (Sandbox Code Playgroud)

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

T.J*_*der 4

即使抛开年度快照规范发布后添加的现代功能(1、2 ,类声明仍然需要类范围来正确处理类内的类绑定。

\n

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

\n

\r\n
\r\n
class Example1 { }\nconsole.log(typeof Example1); // "function"\nconst x = class Example2 { };\nconsole.log(typeof Example2); // "undefined"
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

虽然这是事实,但仍然会创建类作用域中的内部绑定,这对于在其中正确解析类绑定非常重要。类中的构造函数和方法不应依赖于类绑定的外部绑定,尤其是因为该外部绑定是可变的

\n

\r\n
\r\n
"use strict";\n// Class declaration, creates binding in the current scope\nclass Example {\n    method() {\n        // Note this uses `Example`\n        return new Example();\n    }\n}\n\n// **BUT**, code can change that binding\nconst OldExample = Example;\nExample = {};\n\nconst e1 = new OldExample();\n// Should this fail because `Example` has been reassigned, but it\'s used by `method`?\nconst e2 = e1.method();\n// No, it works just fine\nconsole.log(e2 instanceof OldExample); // true
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

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

\n

正如我所提到的,类字段方法进一步使用了类作用域,但在添加它们之前就需要它。

\n

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

\n
\n
    \n
  • 5.a. 将正在运行的执行上下文的 LexicalEnvironment 设置为 classScope。
  • \n
  • 5.b. 令 superclassRef 为评估 ClassHeritage 的结果。
  • \n
  • 5.c. 将正在运行的执行上下文的 LexicalEnvironment 设置为 env。
  • \n
\n
\n

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

\n

Bergi对此给出了答案,并且与上面的答案相同:以便在ClassHeritagemethod中正确解析类的绑定。他的非常简洁的示例使用了一个尚未纳入该规范的提案(静态方法),但它仍然说明了这一点:

\n

\r\n
\r\n
// shows the class\n(class X extends (class Y { static logX() { console.log(X); } }) { }).logX();
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

X这显示了扩展 class的 class 的类表达式Y,其中 class是在ClassHeritageY语法 production\xc2\xa0\xe2\x80\x94中定义的,并引用! (这是一个好主意吗?可能不是。但可能存在非常尖锐的边缘情况。)X

\n

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

\n

\r\n
\r\n
const x = new class X extends (\n    class Y {\n        logX() {\n            console.log(X);\n        }\n    }\n) {\n};\nx.logX();               // shows the class\nconsole.log(typeof X);  // undefined
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

所以,即使是类声明也可以使用classScope,甚至在添加类字段等之前。

\n