即使撇开在该规范的里程碑副本发布(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 fields和methods进一步使用了类作用域,但在添加它们之前就已经需要了.
一个棘手的问题是你在 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.