简体   繁体   English

EcmaScript 规范中 classScope 的目的是什么?

[英]What is the purpose of classScope in the EcmaScript specification?

I was reading the steps in the specification (12th edition) that occur when a class declaration or expression is used, and saw that it starts off by creating a class scope at section 15.7.7 Runtime Semantics: ClassDefinitionEvaluation :我正在阅读使用类声明或表达式时发生的规范(第 12 版)中的步骤,并看到它通过在第15.7.7节运行时语义:ClassDefinitionEvaluation中创建类范围开始:

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

I can then see that this algorithm in the spec then creates and sets the classBinding as a binding of the classScope envrionment record (at steps 3a , and 19a ), but other than that, I can't seem to see what else it is being used for.然后我可以看到规范中的这个算法然后创建 classBinding 并将其设置为classBinding环境记录的绑定(在步骤3a19a处),但classScope ,我似乎看不到它还有什么用于。 If that's the case, it seems like creating a classScope would only be needed for class expressions (where the class name is available from only within the class and not in the scope its declared) and not class declarations.如果是这种情况,似乎只需要为类表达式创建一个classScope (其中类名只能在类内而不是在其声明的范围内)而不是类声明。 I'm confused as to why classScope is created for class declarations also ( 15.7.8 for class declarations runs the above algorithm 15.7.7 ) when the class name binding is added to the surrounding scope, perhaps it's something to do with when a class uses extends ?当将类名绑定添加到周围范围时,为什么还要为类声明创建classScope (类声明的15.7.8运行上述算法15.7.7 ),我对此感到困惑,也许这与类时有关使用extends

Even setting aside modern features added after that annual snapshot spec was published ( 1 , 2 ), class declarations still needed class scope to handle the class binding correctly within the class.即使撇开年度快照规范发布后添加的现代特性( 1、2 ),声明仍然需要类范围来正确处理类中的类绑定。

You noted that a class declaration creates a binding for the class name in the scope containing the declaration, whereas a class expression doesn't:您注意到类声明在包含该声明的范围内为类名创建绑定,而类表达式不会:

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

While that's true, the inner binding in the class scope is still created, and that's important for resolving the class binding correctly within it.虽然这是真的,但仍会创建类范围内的内部绑定,这对于在其中正确解析类绑定很重要。 The constructor and methods within the class shouldn't have to rely on that external binding for the class binding, not least because that external binding is 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

If method relied on the outer binding of Example , it would be using the wrong thing when it did new Example .如果method依赖于Example的外部绑定,那么它在执行new Example时会使用错误的东西。 But thanks to class scope, it doesn't rely on that binding, it relies on the inner binding for it within the class scope (which is immutable).但是由于类范围,它不依赖于该绑定,它依赖于类范围内的内部绑定(这是不可变的)。

As I mentioned, class fields and methods make further use of the class scope, but it was needed even before they were added.正如我所提到的,类字段方法进一步利用了类作用域,但在添加它们之前就需要它。

One tricky bit is this sequence you pointed out in a comment on my previous incorrect answer:一个棘手的问题是您在对我之前的错误答案的评论中指出的这个序列:

  • 5.a. 5.a. Set the running execution context's LexicalEnvironment to classScope.将运行执行上下文的 LexicalEnvironment 设置为 classScope。
  • 5.b. 5.b。 Let superclassRef be the result of evaluating ClassHeritage.让 superclassRef 是评估 ClassHeritage 的结果。
  • 5.c. 5.c。 Set the running execution context's LexicalEnvironment to env.将运行执行上下文的 LexicalEnvironment 设置为 env。

Why set the running execution context's LexicalEnvironment to classScope just to evaluate ClassHeritage ?为什么将运行执行上下文的 LexicalEnvironment 设置为 classScope 只是为了评估ClassHeritage

Bergi came up with the answer to that, and it's the same answer as for method above: So that the class's binding is properly resolved within ClassHeritage . Bergi想出了这个问题的答案,它与上述method的答案相同:因此类的绑定在ClassHeritage中得到正确解决。 His wonderfully-succinct example uses a proposal (static methods) that hadn't landed in that spec yet, but it still makes the point:他非常简洁的示例使用了一个尚未出现在该规范中的提案(静态方法),但它仍然说明了这一点:

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

That shows a class expression for class X that extends class Y , where class Y is defined within the ClassHeritage syntax production — and refers to X !这显示了扩展类Y的类X的类表达式,其中类YClassHeritage语法产生式中定义 - 并引用X (Is this a good idea? Probably not . But there may be very edgy edge cases.) (这是个好主意吗?可能不是。但可能会有非常前卫的边缘情况。)

Just for clarity, let's expand that a bit and stick to features in the spec you linked to:为清楚起见,让我们稍微扩展一下并坚持您链接到的规范中的功能:

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

So there we are, even class declarations have use for classScope , even before class fields and such were added.因此,即使在添加类字段等之前,甚至类声明也可以用于classScope

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM