我正在阅读使用类声明或表达式时发生的规范(第 12 版)中的步骤,并看到它通过在部分 中创建类范围开始。 15.7.7运行时语义:ClassDefinitionEvaluation :
1. Let env be the LexicalEnvironment of the running execution context.
2. Let classScope be NewDeclarativeEnvironment(env).
...
然后我可以看到规范中的这个算法然后创建并设置 classBinding
作为 classScope
的绑定(bind)环境记录(在步骤 3a 和 19a 处),但除此之外,我似乎看不到它还有什么用途。如果是这样的话,似乎创建了一个 classScope
只需要类表达式(类名只能在类内而不是在其声明的范围内)而不是类声明。我很困惑为什么classScope
当类名绑定(bind)添加到周围范围时,也为类声明创建( 15.7.8 用于类声明运行上述算法 15.7.7 ),也许这与类使用时有关 extends
?
最佳答案
即使撇开年度快照规范发布后添加的现代特性(1,2),类声明仍然需要类范围来正确处理类中的类绑定(bind)。
您注意到类声明在包含该声明的范围内为类名创建绑定(bind),而类表达式不会:
class Example1 { }
console.log(typeof Example1); // "function"
const x = class Example2 { };
console.log(typeof Example2); // "undefined"
虽然这是真的,但仍会创建类范围内的内部绑定(bind),这对于在其中正确解析类绑定(bind)很重要。类中的构造函数和方法不必依赖于类绑定(bind)的外部绑定(bind),尤其是因为外部绑定(bind)是可变的:
"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
的外部绑定(bind),它会使用错误的东西new Example
.但是由于类范围,它不依赖于该绑定(bind),它依赖于类范围内的内部绑定(bind)(这是不可变的)。正如我所提到的,class fields和 methods进一步使用类范围,但在添加它们之前就需要它。
一个棘手的问题是您在对我之前的错误答案的评论中指出的这个序列:
- 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
的答案相同上图:以便在 ClassHeritage 中正确解析类的绑定(bind)。他非常简洁的示例使用了一个尚未出现在该规范中的提案(静态方法),但它仍然说明了这一点:// shows the class
(class X extends (class Y { static logX() { console.log(X); } }) { }).logX();
这显示了类
X
的类表达式扩展类 Y
, 其中类 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
因此,即使在添加类字段等之前,甚至类声明也可用于 classScope。
关于javascript - EcmaScript 规范中 classScope 的目的是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72214738/