22# ECMAScript class property access expressions
33
44Class access expressions seek to simplify access to static members of a class as well as provide
5- access to static members of a class that cannot be named :
5+ access to static members of a class when that class is unnamed :
66
77``` js
88class C {
@@ -150,22 +150,16 @@ class C {
150150- During ClassDefinitionEvaluation, the class constructor (<var >F</var >) is set as the \[\[ ClassObject]] on the method.
151151- During NewFunctionEnvironment, the \[\[ ClassObject]] is copied from the method (<var >F</var >) to <var >envRec</var >.\[\[ ClassObject]] .
152152- Arrow functions use the \[\[ ClassObject]] of their containing lexical environment (similar to ` super ` and ` this ` ).
153- - A new Class Reference type is added with properties similar to Super Reference.
154- - When evaluating `` ClassProperty: `class` `.` IdentifierName `` we return a new Class Reference with the following properties:
153+ - When evaluating `` ClassProperty: `class` `.` IdentifierName `` we return a new Reference with the following properties:
155154 - The referenced name component is the StringValue of _ IdentifierName_ .
156155 - The base value component is the \[\[ ClassObject]] of GetThisEnvironment().
157- - The actualThis component is either:
158- - If the containing method is static, the current ` this ` binding.
159- - Else, the \[\[ ClassObject]] of GetThisEnvironment().
160- - When evaluating `` ClassProperty: `class` `[` Expression `]` `` we return a new Class Reference with the following properties:
156+ - When evaluating `` ClassProperty: `class` `[` Expression `]` `` we return a new Reference with the following properties:
161157 - The referenced name component is the result of calling ?ToPropertyKey on the result of calling GetValue on the result of evaluating _ Expression_ .
162158 - The base value component is the \[\[ ClassObject]] of GetThisEnvironment().
163- - The actualThis component is either:
164- - If the containing method is static, the current ` this ` binding.
165- - Else, the \[\[ ClassObject]] of GetThisEnvironment().
166- - GetThisValue(<var >V</var >) would be modified to add an optional <var >calling</var > argument that is set to ** true** during EvaluateCall.
167- - GetThisValue(<var >V</var >, ** true** ) returns the thisValue component of a Class Reference in the same way that it does for a Super Reference.
168- - GetThisValue(<var >V</var >, ** false** ) returns the base value component of a Class Reference.
159+ - When evaluating `` ClassProperty: `class` `.` PrivateIdentifier `` we perform the following steps:
160+ 1 . Let ` fieldNameString ` be the StringValue of _ PrivateIdentifier_ .
161+ 2 . Let ` bv ` be the \[\[ ClassObject]] of GetThisEnvironment().
162+ 3 . Return ?[ MakePrivateReference] ( https://tc39.es/proposal-class-fields/#sec-makeprivatereference ) ( ` bv ` , ` fieldNameString ` )
169163
170164<!-- #endregion:semantics-->
171165
@@ -253,7 +247,7 @@ This behavior provides the following benefits:
253247
254248### Method Invocation
255249
256- Invoking ` class.x() ` in a static method uses the current ` this ` as the receiver (similar to the behavior of ` super.x() ` ) :
250+ Invoking ` class.x() ` in a method, an initializer, or in the constructor uses the value of containing lexical class as the receiver :
257251
258252``` js
259253class Base {
@@ -263,47 +257,24 @@ class Base {
263257 static g () {
264258 class .f ();
265259 }
260+ h () {
261+ class .f ();
262+ }
266263}
267264class Sub extends Base {
268265}
269266
270267Base .g (); // this: Base, class: Base
271268Sub .g (); // this: Sub, class: Base
272269Base .g .call ({ name: " Other" }); // this: Other, class: Base
273- ```
274-
275- This behavior provides the following benefits:
276270
277- - Method invocation preserves the ` this ` receiver to allow for overriding static methods in a subclass.
278- - Invocation behavior is similar to ` super.x() ` , so should be less surprising to users.
279-
280- Invoking ` class.x() ` in a non-static method or the constructor uses the value of containing lexical class as the receiver:
281-
282- ``` js
283- class Base {
284- static f () {
285- console .log (` this.name: ${ this .name } , class.name: ${ class .name } )` );
286- }
287- g () {
288- class .f ();
289- }
290- }
291- class Sub extends Base {
292- }
293-
294- let b = new Base ();
271+ let b = new Base ();
295272let s = new Sub ();
296-
297- b .g (); // this: Base, class: Base
298- s .g (); // this: Base, class: Base
299- Base .prototype .g .call ({ name: " Other" }); // this: Base, class: Base
273+ b .h (); // this: Base, class: Base
274+ s .h (); // this: Sub, class: Base
275+ b .h .call ({ name: " Other" }); // this: Other, class: Base
300276```
301277
302- This behavior provides the following benefits:
303-
304- - Since instances will not have the lexical class constructor in their prototype hierarchy (other than through narrow corner cases),
305- users would not expect the lexical ` this ` to be passed as the receiver from non-static methods. This behavior is the most
306- intuitive and least-surprising behavior for users.
307278<!-- #endregion:examples-->
308279
309280<!-- #region:api-->
@@ -324,6 +295,7 @@ MemberExpression[Yield, Await] :
324295ClassProperty[Yield, Await] :
325296 `class` `[` Expression[+In, ?Yield, ?Await] `]`
326297 `class` `.` IdentifierName
298+ `class` `.` PrivateIdentifier
327299```
328300<!-- #endregion:grammar-->
329301
@@ -336,7 +308,7 @@ This proposal can easily align with the current class fields proposal, providing
336308``` js
337309class Base {
338310 static counter = 0 ;
339- id = class .counter ++ ; // Assignment, so `Base` is used as `this`
311+ id = class .counter ++ ; // `Base` is used as `this`
340312}
341313
342314class Sub extends Base {
@@ -348,35 +320,6 @@ console.log(Base.counter); // 2
348320console .log (Sub .counter ); // 2
349321```
350322
351- ## Class Private Methods
352-
353- This proposal can also align with the current proposals for class private methods, providing access without introducing
354- TypeErrors due to incorrect ` this ` while preserving the ability for subclasses to override behavior:
355-
356- ``` js
357- class Base {
358- static a () {
359- console .log (" Base.a()" );
360- class .#b ();
361- }
362- static #b () {
363- console .log (" Base.#b()" );
364- this .c ();
365- }
366- static c () {
367- console .log (" Base.c()" );
368- }
369- }
370-
371- class Sub extends Base {
372- static c () {
373- console .log (" Sub.c()" );
374- }
375- }
376-
377- Base .a (); // Base.a()\nBase.#b()\nBase.c()
378- Sub .a (); // Base.a()\nBase.#b()\nSub.c()
379- ```
380323
381324## Class Private Fields
382325
@@ -399,6 +342,65 @@ console.log(Sub.increment()); // 1
399342console .log (Base .increment ()); // 2
400343```
401344
345+ ## Class Private Methods
346+
347+ One of the benefits of ` class ` access expressions is that they guarantee the correct reference is used when accessing static private members. Special care must be taken, however, when invoking private and non-private static methods using ` class ` access expressions, as the ` this ` binding within the invoked method will be the lexical class declaration:
348+
349+ ``` js
350+ class Base {
351+ static #counter = 0 ;
352+ static #increment () {
353+ class .#counter++ ;
354+ this .printCounter ();
355+ }
356+ static doIncrement () {
357+ class .#increment ();
358+ }
359+ static printCounter () {
360+ console .log (class .#counter);
361+ }
362+ }
363+ class Sub extends Base {
364+ static printCounter () {
365+ console .log (" Custom Counter" );
366+ super .printCounter ();
367+ }
368+ }
369+
370+ Base .doIncrement (); // prints: 1
371+ Sub .doIncrement (); // prints: 2
372+ ```
373+
374+ In the example above, ` Sub ` 's overriden ` printCounter ` is never invoked. Such method calls would need to be rewritten:
375+
376+ ``` js
377+ // option 1:
378+ class Base {
379+ ...
380+ static #increment () {
381+ ...
382+ }
383+ static doIncrement () {
384+ class .#increment .call (this );
385+ }
386+ }
387+
388+ // option 2:
389+ class Base {
390+ ...
391+ static #increment (C ) {
392+ ...
393+ C .printCounter ();
394+ }
395+ ...
396+ static doIncrement () {
397+ class .#increment (this );
398+ }
399+ }
400+ ```
401+
402+ This is due to the fact that ` class. ` in this example is essentially a substitute for ` Base. ` , therefore ` Base ` becomes the receiver in these method calls.
403+
402404
403405<!-- #region:references-->
404406<!--
0 commit comments