-1

From MDN Web Docs:

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 ClassWithPrivateStaticField class) when we try to call Subclass.publicStaticMethod(), and so causes a TypeError.

class ClassWithPrivateStaticField {
  static #privateStaticField = 42;

  static publicStaticMethod() {
    return this.#privateStaticField;
  }
}

class Subclass extends ClassWithPrivateStaticField {}

Subclass.publicStaticMethod();
// TypeError: Cannot read private member #privateStaticField from an
// object whose class did not declare it

[…]

You are advised to always access private static fields through the class name, not through this, so inheritance doesn’t break the method.

So this cannot be used in static methods to access private static members (the example for fields above also applies to methods) because it breaks for subclasses.

However this can be used in instance methods to access private instance members (the example for fields below also applies to methods) because it works for subclass instances:

class ClassWithPrivateInstanceField {
  #privateInstanceField = 42;

  publicInstanceMethod() {
    return this.#privateInstanceField;
  }
}

class Subclass extends ClassWithPrivateInstanceField {}

new Subclass().publicInstanceMethod(); // 42

Why is access to private static members through a subclass forbidden?

Géry Ogam
  • 6,336
  • 4
  • 38
  • 67
  • Doesn't it say, "so inheritance doesn't break the method" at the end of your excerpt? – kelsny Apr 07 '23 at 22:47
  • `this` only exists when a method is called on an instance. Static methods can be called without an instance, so there's no `this` to indirect through. – Barmar Apr 07 '23 at 22:51
  • @pink I’m talking about the restriction of not inheriting private static fields, not about the restriction of accessing static private fields through the class name which is the consequence. – Géry Ogam Apr 07 '23 at 22:54
  • 1
    @Barmar I beg to differ, `this` does exist in static methods: `BaseClass.basePublicStaticMethod();` works for example. – Géry Ogam Apr 07 '23 at 22:57
  • 1
    Right. I think it's because accesing normal fields follows the prototype chain, but private fields doesn't -- that's what makes them private. – Barmar Apr 07 '23 at 22:59
  • @Barmar But private *instance* fields *are* inherited (cf. `new SubClass().basePublicInstanceMethod();`). So why not private *static* fields? – Géry Ogam Apr 07 '23 at 23:01
  • 2
    Because instance fields are properties of the instance, they're not accessed through the prototype. The prototype chain is used for methods and static fields.s – Barmar Apr 07 '23 at 23:04
  • @Barmar Right. ‘I think it's because accessing normal fields follows the prototype chain, but private fields doesn't -- that's what makes them private.’ What makes them private to me is that they are not accessible from a subclass (e.g. a static method defined in `SubClass` cannot have `this.#PRIVATE_STATIC_FIELD`). But preventing `this.#PRIVATE_STATIC_FIELD` in a static method defined in `BaseClassWithPrivateStaticField` is unnecessarily too restrictive to me. – Géry Ogam Apr 07 '23 at 23:33
  • What I meant was that's how privacy is implemented. – Barmar Apr 07 '23 at 23:34
  • @Barmar Yes, but what is the benefit of making static privacy too restrictive? The drawback is that you cannot use `this` anymore, so I assume that there must at least be a benefit. – Géry Ogam Apr 07 '23 at 23:50
  • 1
    I don't feel like it was a deliberate design decision, but something that fell out of the implementation. By making them private by disabling use of the prototype chain, you can't access them through `this`. – Barmar Apr 08 '23 at 04:11

1 Answers1

0

On the contrary, private instance fields are inherited

No, they aren't either. Private fields are not inherited prototypically, accessing them does not follow the prototype chain.

The difference between static and instance fields is that the latter are created by the constructor, which is "inherited", so they are also created on subclass instances by default (in the super() call). There is no equivalent feature for static properties, no code from the parent class runs (and could create private static fields) when a child class extends it.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • ‘Private fields are not inherited prototypically, accessing them does not follow the prototype chain.’ Right, I shouldn’t have said that private instance fields are *inherited*, I should have said that they are *accessible from instance methods of the class that declare them*. – Géry Ogam Apr 08 '23 at 00:06
  • ‘There is no equivalent feature for static properties’ Public static fields are inherited, so why not private static fields? What is the benefit of that restriction? The drawback is that you cannot use `this` anymore. To me, a private property should mean a property accessible only within a class body; it shouldn’t mean a property accessible only within a class body *through that class or an instance constructed by that class* (this is too restrictive). – Géry Ogam Apr 08 '23 at 01:11
  • @GéryOgam Static properties are inherited prototypically, they don't get re-created on child classes either. Regarding accessibility from methods, you rarely want to use `this` anyway in static methods, explicitly referring to the class works for more invocations and leads to clearer code. – Bergi Apr 08 '23 at 11:10
  • Personally, I don't see any use in static private properties at all. They're equivalent to a local variable declared next to the `class`, and should be treated as such. And they should generally be `const`ant since static methods should not carry any state, so there's no benefit in inheriting them either. – Bergi Apr 08 '23 at 11:12
  • Thanks for sharing your view. But you don’t see any benefit with the current restriction either. The major drawback of the current restriction is that it complicates the language for programmers by creating an exception: you can access properties through `this` *except for private static fields*. So I’m going to open a feature request to lift that restriction which seems accidental, as Barmar explained in his last comment on my post. – Géry Ogam Apr 08 '23 at 11:29
  • No, you should generally [not use `this` to access static properties unless you want them to be overridable](https://stackoverflow.com/a/28648214/1048572), and private fields certainly aren't meant to be overridden. – Bergi Apr 08 '23 at 14:01
  • Using `this` does not make private fields overridable so I fail to see the problem of using it. For example, using `this.#privateField` in a class will never access a `#privateField` of a subclass (cf. Python where using `self.__private_field` in a class `A` still accesses `_A__private_field` and not `_B__private_field` when `self` is `B`). – Géry Ogam Apr 08 '23 at 15:03
  • "*using `this.#privateField` in a class body will never access a `#privateField` of a subclass*" - are you referring to normal methods, static methods, or both here? But in either case, `this` is always dynamic and its value depends on how the method was invoked. Just don't use it in static methods, there's no advantage to it anyway. – Bergi Apr 08 '23 at 15:11
  • Re Python, that still does access `_A__private_field` *on `self`* which is a `B` instance (and in fact it can be overridden if you wanted to). – Bergi Apr 08 '23 at 15:13
  • ‘are you referring to normal methods, static methods, or both here?’ Both. ‘Just don't use it in static methods, there's no advantage to it anyway.’ The advantage is that programmers don’t have to remember not to use `this` for accessing private static members. However I don’t see the advantage of forcing this arbitrary rule. Can you give one? – Géry Ogam Apr 08 '23 at 15:17
  • In normal methods, `this.#privateField` actually *does* access the private field on an instance of the subclass if the method is inherited, so there it makes sense to use `this` - the data depends on the particular instance. "*programmers don’t have to remember not to use `this` for accessing private static members*" - the rule is not arbitrary, and its not specific to private fields: programmers should remember not to use `this` for accessing any `static` members. If they do follow this rule, they won't get any errors from private fields, and no weird behaviour from inherited properties. – Bergi Apr 08 '23 at 15:23
  • In Python, `__private_field` in `A` is name mangled to `_A__private_field`, so yes it can be overridden in a subclass, but that’s not how private arguments are intended to be used: in a subclass `B` you would use `__private_field`, which would be name mangled to `_B__private_field`. So no, if you use Python correctly you cannot override private fields. And in JavaScript private fields are really private so there is no workaround to override them anyway. – Géry Ogam Apr 08 '23 at 15:30
  • Exactly, they are really private, which is the reason why static private fields don't exist on subclasses. – Bergi Apr 08 '23 at 15:36
  • By the way, I never mentioned it in my original post but the `this` restriction preventing private static fields from being inherited also applies to private static methods (cf. the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields#private_methods)). In other words, it applies to *private static members*. – Géry Ogam Apr 08 '23 at 15:46
  • ‘programmers should remember not to use `this` for accessing any static members.’ I beg to differ: if you want *public* static members to be overridable you *have* to use `this`. It’s only *private* static members that can’t be overridden so it should not make a difference for them if you use `this` or the class name (except that JavaScript arbitrarily raises a `TypeError` in the first case, which is what I’m questioning). – Géry Ogam Apr 08 '23 at 15:58
  • "*If you want public static members to be overridable you* have *to use `this`.*" - yes, but *only* when they should be overridable. By default, for both public and private static members, the class should be referred to directly by its name. When using `this`, it can always happen that the method is invoked on an object which does not have the member, and you need to deal with that. Throwing a `TypeError` is certainly better than failing silently by returning `undefined`… – Bergi Apr 08 '23 at 16:06
  • ‘When using `this`, it can always happen that the method is invoked on an object which does not have the member, and you need to deal with that.’ Could you provide an example? – Géry Ogam Apr 08 '23 at 16:10
  • We've seen one in your question (`SubClass.basePublicStaticMethod()`), but it could also be `BaseClass.publicStaticMethod.call({})`, `setTimeout(BaseClass.publicStaticMethod)` or anything else. – Bergi Apr 08 '23 at 16:14
  • ‘When using `this`, it can always happen that the method is invoked on an object which does not have the member, and you need to deal with that. Throwing a `TypeError` is certainly better than failing silently by returning `undefined`…’ Yes, raising a `TypeError` makes sense when the private property doesn’t exist, so for `BaseClassWithPrivateStaticField. basePublicStaticMethod.call({})`. But not for `SubClass.basePublicStaticMethod()` since in that case the private property *does* exist on the `BaseClassWithPrivateStaticField` superclass. – Géry Ogam Apr 08 '23 at 18:29