From 58ba52e1264f4062cdc8219ceff95bb1a914a383 Mon Sep 17 00:00:00 2001 From: "Eric A. Meyer" Date: Thu, 3 Jun 2021 14:47:25 -0400 Subject: [PATCH 01/10] Recovered previous state of private class features page --- .../classes/private_class_fields/index.html | 261 +++++++++++------- .../classes/public_class_fields/index.html | 23 +- 2 files changed, 176 insertions(+), 108 deletions(-) diff --git a/files/en-us/web/javascript/reference/classes/private_class_fields/index.html b/files/en-us/web/javascript/reference/classes/private_class_fields/index.html index 6e09d14f35cb225..8b7b7b26df99ed5 100644 --- a/files/en-us/web/javascript/reference/classes/private_class_fields/index.html +++ b/files/en-us/web/javascript/reference/classes/private_class_fields/index.html @@ -1,119 +1,201 @@ --- -title: Private class fields +title: Private class features slug: Web/JavaScript/Reference/Classes/Private_class_fields tags: - Classes +- Private - JavaScript - Language feature -browser-compat: javascript.classes.private_class_fields ---
{{JsSidebar("Classes")}}
-

Class properties are public by default and can be examined or modified outside the +

Class properties are public by default, and can be examined or modified outside the class. There is however a - stage 3 proposal to allow defining private class fields using a hash - # prefix.

+ stage 3 proposal to allow private class features by using a hash + # prefix. The privacy encapsulation of these class features is + enforced by JavaScript itself.

-

Syntax

+

Basic Syntax

class ClassWithPrivateField {
-  #privateField
+  #privateField;
 }
 
 class ClassWithPrivateMethod {
   #privateMethod() {
-    return 'hello world'
+    return 'hello world';
   }
 }
 
 class ClassWithPrivateStaticField {
-  static #PRIVATE_STATIC_FIELD
+  static #PRIVATE_STATIC_FIELD;
+}
+
+class ClassWithPrivateStaticMethod {
+  static #privateStaticMethod() {
+    return 'hello world';
+  }
 }
 

Examples

-

Private static fields

+

Private instance fields

+ +

Private instance fields are declared with # names (pronounced + "hash names"), which are identifiers prefixed with #. The + # is a part of the name itself. Private fields are accessible on + the class constructor from inside the class + declaration itself. They are used for declaration of field names as well + as for accessing a field’s value.

+ +

It is a syntax error to refer to # names from out of scope. + It is also a syntax error to refer to private fields + that were not declared before they were called, or to attempt to remove + declared fields with delete.

+ +
class ClassWithPrivateField {
+  #privateField;
+
+  constructor() {
+    this.#privateField = 42;
+    delete this.#privateField;   // Syntax error
+    this.#undeclaredField = 444; // Syntax error
+  }
+}
 
-

Private fields are accessible on the class constructor from inside the class - declaration itself.

+const instance = new ClassWithPrivateField() +instance.#privateField === 42; // Syntax error +
+ +

Like public fields, private fields are added at construction time in a base class, or at the point where super() is invoked in a subclass.

-

The limitation of static variables being called by only static methods still holds.

+
class ClassWithPrivateField {
+  #privateField;
+
+  constructor() {
+    this.#privateField = 42;
+  }
+}
+
+class SubClass extends ClassWithPrivateField {
+  #subPrivateField;
+
+  constructor() {
+    super();
+    this.#subPrivateField = 23;
+  }
+}
+
+new SubClass();
+// SubClass {#privateField: 42, #subPrivateField: 23}
+
+ + +

Private static fields

+ +

Private static fields are added to the class constructor at class evaluation time. + The limitation of static variables being called by only static methods still holds.

class ClassWithPrivateStaticField {
-  static #PRIVATE_STATIC_FIELD
+  static #PRIVATE_STATIC_FIELD;
 
   static publicStaticMethod() {
-    ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42
-    return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD
+    ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42;
+    return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD;
   }
 }
 
-console.assert(ClassWithPrivateStaticField.publicStaticMethod() === 42)
- -

Private static fields are added to the class constructor at class evaluation time.

+console.log(ClassWithPrivateStaticField.publicStaticMethod() === 42); +// true + -

There is a provenance restriction on private static fields. Only the class which - defines the private static field can access the field.

-

This can lead to unexpected behavior when using this.

+

There is a provenance restriction on private static fields: Only the class which + defines the private static field can access the field. This can lead to unexpected behavior when using this. + In the following example, this refers to the SubClass class (not + the BaseClassWithPrivateStaticField class) when we try to call + SubClass.basePublicStaticMethod(), and so causes a TypeError.

class BaseClassWithPrivateStaticField {
-  static #PRIVATE_STATIC_FIELD
+  static #PRIVATE_STATIC_FIELD;
 
   static basePublicStaticMethod() {
-    this.#PRIVATE_STATIC_FIELD = 42
-    return this.#PRIVATE_STATIC_FIELD
+    this.#PRIVATE_STATIC_FIELD = 42;
+    return this.#PRIVATE_STATIC_FIELD;
   }
 }
 
-class SubClass extends BaseClassWithPrivateStaticField { }
+class SubClass extends BaseClassWithPrivateStaticField { };
 
-let error = null
+let error = null;
 
 try {
   SubClass.basePublicStaticMethod()
-} catch(e) { error = e}
+} catch(e) { error = e};
 
-console.assert(error instanceof TypeError)
+console.log(error instanceof TypeError);
+// true
+console.log(error);
+// TypeError: Cannot write private member #PRIVATE_STATIC_FIELD
+// to an object whose class did not declare it
 
-

Private instance fields

-

Private instance fields are declared with # names (pronounced - "hash names"), which are identifiers prefixed with #. The - # is a part of the name itself. It is used for declaration and accessing as - well.

+

Private methods

-

The encapsulation is enforced by the language. It is a syntax error to refer to - # names from out of scope.

+

Private instance methods

-
class ClassWithPrivateField {
-  #privateField
+

Private instance methods are methods available on class instances whose access is + restricted in the same manner as private instance fields.

+ +
class ClassWithPrivateMethod {
+  #privateMethod() {
+    return 'hello world';
+  }
+
+  getPrivateMessage() {
+    return this.#privateMethod();
+  }
+}
+
+const instance = new ClassWithPrivateMethod();
+console.log(instance.getPrivateMessage());
+// hello world
+ +

Private instance methods may be generator, async, or async generator functions. Private + getters and setters are also possible, although not in generator, async, or + async generator forms.

+ +
class ClassWithPrivateAccessor {
+  #message;
+
+  get #decoratedMessage() {
+    return `🎬${this.#message}🛑`;
+  }
+  set #decoratedMessage(msg) {
+    this.#message = msg;
+  }
 
   constructor() {
-    this.#privateField = 42
-    this.#randomField = 444 // Syntax error
+    this.#decoratedMessage = 'hello world';
+    console.log(this.#decoratedMessage);
   }
 }
 
-const instance = new ClassWithPrivateField()
-instance.#privateField === 42 // Syntax error
+new ClassWithPrivateAccessor();
+// 🎬hello world🛑
 
-

Private methods

-

Private static methods

Like their public equivalent, private static methods are called on the class itself, not instances of the class. Like private static fields, they are only accessible from inside the class declaration.

-

Private static methods may be generator, async, and async generator functions.

-
class ClassWithPrivateStaticMethod {
   static #privateStaticMethod() {
-    return 42
+    return 42;
   }
 
   static publicStaticMethod1() {
@@ -125,15 +207,20 @@ 

Private static methods

} } -console.assert(ClassWithPrivateStaticMethod.publicStaticMethod1() === 42); -console.assert(ClassWithPrivateStaticMethod.publicStaticMethod2() === 42); +console.log(ClassWithPrivateStaticMethod.publicStaticMethod1() === 42); +// true +console.log(ClassWithPrivateStaticMethod.publicStaticMethod2() === 42); +// true
-

This can lead to unexpected behavior when using this. In - the following example this refers to the Derived class (not - the Base class) when we try to call - Derived.publicStaticMethod2(), and thus exhibits the same "provenance - restriction" as mentioned above:

+

Private static methods may be generator, async, and async generator functions.

+ +

The same provenance restriction previously mentioned for private static fields holds + for private static methods, and similarly can lead to unexpected behavior when using + this. + In the following example, when we try to call Derived.publicStaticMethod2(), + this refers to the Derived class (not + the Base class) and so causes a TypeError.

class Base {
   static #privateStaticMethod() {
@@ -149,67 +236,39 @@ 

Private static methods

class Derived extends Base {} -console.log(Derived.publicStaticMethod1()); // 42 -console.log(Derived.publicStaticMethod2()); // TypeError +console.log(Derived.publicStaticMethod1()); +// 42 +console.log(Derived.publicStaticMethod2()); +// TypeError: Cannot read private member #privateStaticMethod +// from an object whose class did not declare it
-

Private instance methods

- -

Private instance methods are methods available on class instances whose access is - restricted in the same manner as private instance fields.

- -
class ClassWithPrivateMethod {
-  #privateMethod() {
-    return 'hello world'
-  }
-
-  getPrivateMessage() {
-    return this.#privateMethod()
-  }
-}
-
-const instance = new ClassWithPrivateMethod()
-console.log(instance.getPrivateMessage())
-// expected output: "hello world"
- -

Private instance methods may be generator, async, or async generator functions. Private - getters and setters are also possible:

- -
class ClassWithPrivateAccessor {
-  #message
-
-  get #decoratedMessage() {
-    return `✨${this.#message}✨`
-  }
-  set #decoratedMessage(msg) {
-    this.#message = msg
-  }
-
-  constructor() {
-    this.#decoratedMessage = 'hello world'
-    console.log(this.#decoratedMessage)
-  }
-}
-
-new ClassWithPrivateAccessor();
-// expected output: "✨hello world✨"
-

Specifications

-{{Specifications}} + + + + + + + + + + + +
Specification
{{SpecName('Public and private instance fields', '#prod-FieldDefinition', + 'FieldDefinition')}}

Browser compatibility

-

{{Compat}}

+

{{Compat("javascript.classes.private_class_fields")}}

See also

-

If you wish to read private data from outside a class, you must first invent a method or other function to return it. We had already done that with the current() getter that returns the current value of #count, but #init is locked away. Unless we add something like a getInit() method to the class, we can’t even see the initial value from outside the class, let alone alter it, and the compiler will throw a fit if we try.

+

If you wish to read private data from outside a class, you must first invent a method or other function to return it. We had already done that with the current() getter that returns the current value of #count, but #init is locked away. Unless we add something like a getInit() method to the class, we can’t even see the initial value from outside the class, let alone alter it, and the compiler will throw errors if we try.

What are the other restrictions around private fields? For one, you can’t refer to a private field you didn’t previously define. You might be used to inventing new fields on the fly in JavaScript, but that just won’t fly with private fields. @@ -145,11 +146,11 @@

Private Fields

-

Private Methods

+

Private methods

Just like private fields, private methods are marked with a leading # and cannot be accessed from outside their class. They’re useful when you have something complex that the class needs to do internally, but it’s something that no other part of the code should be allowed to call.

-

For example, imagine creating HTML custom elements that should do something somewhat complicated when clicked/tapped/otherwise activated. Furthermore, the somewhat complicated things that happen when the element is clicked should be restricted to this class, because no other part of the JavaScript will (or should) ever access it. Therefore, something like:

+

For example, imagine creating HTML custom elements that should do something somewhat complicated when clicked/tapped/otherwise activated. Furthermore, the somewhat complicated things that happen when the element is clicked should be restricted to this class, because no other part of the JavaScript will (or should) ever access it. Therefore, something like:

 class CustomClick extends HTMLElement {
@@ -206,5 +207,5 @@ 

See also

Browser compatibility

-

{{Compat("javascript.classes.private_class_fields")}}

+

{{Compat}}

diff --git a/files/en-us/web/javascript/reference/classes/private_class_fields/index.html b/files/en-us/web/javascript/reference/classes/private_class_fields/index.html index 8b7b7b26df99ed5..33256c638eab18b 100644 --- a/files/en-us/web/javascript/reference/classes/private_class_fields/index.html +++ b/files/en-us/web/javascript/reference/classes/private_class_fields/index.html @@ -6,6 +6,7 @@ - Private - JavaScript - Language feature +browser-compat: javascript.classes.private_class_fields ---
{{JsSidebar("Classes")}}
@@ -15,7 +16,7 @@ # prefix. The privacy encapsulation of these class features is enforced by JavaScript itself.

-

Basic Syntax

+

Syntax

class ClassWithPrivateField {
   #privateField;
@@ -54,7 +55,7 @@ 

Private instance fields

that were not declared before they were called, or to attempt to remove declared fields with delete.

-
class ClassWithPrivateField {
+
class ClassWithPrivateField {
   #privateField;
 
   constructor() {
@@ -111,7 +112,7 @@ 

Private static fields

-

There is a provenance restriction on private static fields: Only the class which +

There is a restriction on private static fields: Only the class which defines the private static field can access the field. This can lead to unexpected behavior when using this. In the following example, this refers to the SubClass class (not the BaseClassWithPrivateStaticField class) when we try to call @@ -215,7 +216,7 @@

Private static methods

Private static methods may be generator, async, and async generator functions.

-

The same provenance restriction previously mentioned for private static fields holds +

The same restriction previously mentioned for private static fields holds for private static methods, and similarly can lead to unexpected behavior when using this. In the following example, when we try to call Derived.publicStaticMethod2(), @@ -246,23 +247,11 @@

Private static methods

Specifications

- - - - - - - - - - - -
Specification
{{SpecName('Public and private instance fields', '#prod-FieldDefinition', - 'FieldDefinition')}}
+{{Specifications}}

Browser compatibility

-

{{Compat("javascript.classes.private_class_fields")}}

+

{{Compat}}

See also

diff --git a/files/en-us/web/javascript/reference/classes/public_class_fields/index.html b/files/en-us/web/javascript/reference/classes/public_class_fields/index.html index d7b690c58e2d466..c91fbadba30049d 100644 --- a/files/en-us/web/javascript/reference/classes/public_class_fields/index.html +++ b/files/en-us/web/javascript/reference/classes/public_class_fields/index.html @@ -5,13 +5,14 @@ - Classes - JavaScript - Language feature +browser-compat: javascript.classes.private_class_fields ---
{{JsSidebar("Classes")}}
-

This page describes experimental features.

+

Note: This page describes experimental features.

-

Both Public and private field declarations are an Both public and private field declarations are an experimental feature (stage 3) proposed at TC39, the JavaScript standards committee.

@@ -274,23 +275,11 @@

Public instance methods

Specifications

- - - - - - - - - - - -
Specification
{{SpecName('Public and private instance fields', '#prod-FieldDefinition', - 'FieldDefinition')}}
+{{Specifications}}

Browser compatibility

-

{{Compat("javascript.classes.public_class_fields")}}

+

{{Compat}}

See also

From 75cba5828625ebf232fdb5a1d7913b8e77cd693a Mon Sep 17 00:00:00 2001 From: "Eric A. Meyer" Date: Wed, 9 Jun 2021 17:49:22 -0400 Subject: [PATCH 05/10] Edits from wbamberg --- .../index.html | 29 ++++++++++--------- .../classes/private_class_fields/index.html | 2 ++ .../classes/public_class_fields/index.html | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/files/en-us/web/javascript/guide/working_with_private_class_features/index.html b/files/en-us/web/javascript/guide/working_with_private_class_features/index.html index 26460f54c81d096..80d44b577abfe42 100644 --- a/files/en-us/web/javascript/guide/working_with_private_class_features/index.html +++ b/files/en-us/web/javascript/guide/working_with_private_class_features/index.html @@ -106,20 +106,6 @@

Private fields

}
-

And you can have static private fields, for things you want to be both private and set in stone at construction.

- -
-class colorMixer {
-  static #red   = "rgba(1,0,0,1)";
-  static #green = "rgba(0,1,0,1)";
-  static #blue  = "rgba(0,0,1,1)";
-  #mixedColor;
-  constructor() {
-    …
-  }
-}
-
-

There is another limitation: you can’t declare private fields or methods via object literals. You might be used to something like this:

@@ -144,6 +130,20 @@ 

Private fields

// "Uncaught SyntaxError: Unexpected identifier"
+

On the other hand, you can have static private fields, for things you want to be both private and set in stone at construction.

+ +
+class colorMixer {
+  static #red   = "rgba(1,0,0,1)";
+  static #green = "rgba(0,1,0,1)";
+  static #blue  = "rgba(0,0,1,1)";
+  #mixedColor;
+  constructor() {
+    …
+  }
+}
+
+

Private methods

@@ -200,6 +200,7 @@

Private methods

See also

diff --git a/files/en-us/web/javascript/reference/classes/private_class_fields/index.html b/files/en-us/web/javascript/reference/classes/private_class_fields/index.html index 33256c638eab18b..84d40032b200c0a 100644 --- a/files/en-us/web/javascript/reference/classes/private_class_fields/index.html +++ b/files/en-us/web/javascript/reference/classes/private_class_fields/index.html @@ -256,6 +256,8 @@

Browser compatibility

See also

diff --git a/files/en-us/web/javascript/reference/classes/public_class_fields/index.html b/files/en-us/web/javascript/reference/classes/public_class_fields/index.html index d7e7a4f4faf5f45..57ba16979ca9381 100644 --- a/files/en-us/web/javascript/reference/classes/public_class_fields/index.html +++ b/files/en-us/web/javascript/reference/classes/public_class_fields/index.html @@ -5,7 +5,7 @@ - Classes - JavaScript - Language feature -browser-compat: javascript.classes.public_class_fields +browser-compat: javascript.classes ---
{{JsSidebar("Classes")}}
@@ -275,4 +275,6 @@

See also

  • The Semantics of All JS Class Elements
  • +
  • Public and private class fields + article at the v8.dev site
  • From c175416a47f7bf0b2e8ad6da51993c85ed87391b Mon Sep 17 00:00:00 2001 From: "Eric A. Meyer" Date: Mon, 21 Jun 2021 14:35:31 -0400 Subject: [PATCH 08/10] Updated BCD front matter for private class fields guide --- .../guide/working_with_private_class_features/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/en-us/web/javascript/guide/working_with_private_class_features/index.html b/files/en-us/web/javascript/guide/working_with_private_class_features/index.html index 80d44b577abfe42..cee0aab7bd00327 100644 --- a/files/en-us/web/javascript/guide/working_with_private_class_features/index.html +++ b/files/en-us/web/javascript/guide/working_with_private_class_features/index.html @@ -5,7 +5,7 @@ - Document - Guide - JavaScript -browser-compat: javascript.classes.private_class_fields +browser-compat: javascript.classes ---
    {{jsSidebar("JavaScript Guide")}}
    From 920b5bda0e6c1472013b9e1179a1f8688ec978c1 Mon Sep 17 00:00:00 2001 From: "Eric A. Meyer" Date: Mon, 21 Jun 2021 14:37:16 -0400 Subject: [PATCH 09/10] Fixed 'properties' usage where needed --- .../reference/classes/private_class_fields/index.html | 2 +- .../reference/classes/public_class_fields/index.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/files/en-us/web/javascript/reference/classes/private_class_fields/index.html b/files/en-us/web/javascript/reference/classes/private_class_fields/index.html index c65d7fcc9919869..1e9a7e9e6de722d 100644 --- a/files/en-us/web/javascript/reference/classes/private_class_fields/index.html +++ b/files/en-us/web/javascript/reference/classes/private_class_fields/index.html @@ -10,7 +10,7 @@ ---
    {{JsSidebar("Classes")}}
    -

    Class properties are {{ jsxref('Classes/Public_class_fields','public') }} by default, but private class members can be created +

    Class fields are {{ jsxref('Classes/Public_class_fields','public') }} by default, but private class members can be created by using a hash # prefix. The privacy encapsulation of these class features is enforced by JavaScript itself.

    diff --git a/files/en-us/web/javascript/reference/classes/public_class_fields/index.html b/files/en-us/web/javascript/reference/classes/public_class_fields/index.html index 57ba16979ca9381..3f852567bd3c9b5 100644 --- a/files/en-us/web/javascript/reference/classes/public_class_fields/index.html +++ b/files/en-us/web/javascript/reference/classes/public_class_fields/index.html @@ -10,8 +10,8 @@
    {{JsSidebar("Classes")}}
    -

    Both static and instance public fields are writable, enumerable, and configurable - properties. As such, unlike their {{ jsxref('Classes/Private_class_fields','private counterparts') }}, they participate in prototype +

    Both static and instance public fields are writable, enumerable, and configurable. + As such, unlike their {{ jsxref('Classes/Private_class_fields','private counterparts') }}, they participate in prototype inheritance.

    Syntax

    From 937f6d862f94013ed4b5301725da493269cd3963 Mon Sep 17 00:00:00 2001 From: "Eric A. Meyer" Date: Mon, 21 Jun 2021 14:39:02 -0400 Subject: [PATCH 10/10] Removed Stage 3 warning from javascript/reference/classes/ --- files/en-us/web/javascript/reference/classes/index.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/files/en-us/web/javascript/reference/classes/index.html b/files/en-us/web/javascript/reference/classes/index.html index 24e349a7cf0b290..142a155f0068d67 100644 --- a/files/en-us/web/javascript/reference/classes/index.html +++ b/files/en-us/web/javascript/reference/classes/index.html @@ -216,10 +216,6 @@

    Instance properties

    Field declarations

    -
    -

    Warning: Public and private field declarations are an experimental feature (stage 3) proposed at TC39, the JavaScript standards committee. Support in browsers is limited, but the feature can be used through a build step with systems like Babel.

    -
    -

    Public field declarations

    With the JavaScript field declaration syntax, the above example can be written as: