Protected and friend access modifier for class members

Not always. In java, a class is a pretty static thing - you can't just create one on the fly. However, you can take the example code I wrote above, throw it in a function, and use it to dynamically create classes at runtime that inherit from whatever instances, specifically for the purpose of extracting protected members.

And, if the protected keyword can't guarantee truly protected access, then one could argue that it's no better than our current means of making protected members.

There's a number of ways to simulate protectedness without strong guarantees. Here's one (ugly-ish) way - it relies on the fact that Enemy shouldn't be instantiated directly, but should instead always be derived. And once derived properly, the protected API won't be accessible by anyone but the derived class. However, nothing stops someone from creating a new Enemy instance and getting access to that protected API without subclassing it.

class Enemy {
  #health
  get health() { return this.#health }

  #protectedApi = {
    reduceHealthBy: amount => {
      this.#health -= amount
    },
  }

  constructor(health) {
    this.#health = health
  }

  static getProtectedApi(instance) {
    const api = instance.#protectedApi
    if (!api) throw new Error('Attempted to call a one-time-use function twice.')
    instance.#protectedApi = null
    return api
  }

  attack(damage, weapon) {
    throw new Error('INTERNAL ERROR: This method should be overwritten')
  }
}

class Slime extends Enemy {
  #enemyApi
  constructor() {
    super(10)
    this.#enemyApi = Enemy.getProtectedApi(this)
  }

  attack(damage, weapon) {
    this.#enemyApi.reduceHealthBy(weapon === 'sword' ? damage * 2 : damage)
  }
}

const mySlime = new Slime()
mySlime.attack(4, 'sword')
console.log(mySlime.health)

You are right, and I am sorry. You convinced me that protected access does not make any sense in Javascript, as one can construct a new class from an object of another class and then derive from that class. That is a conceptual inherent characteristic of prototypical inheritance in Javascript. Maybe I was misleaded by TypeScript, as TypeScript has a protected access modifier, and in TypeScript I created a similar example as Yours:

class A {
  protected value: Number = 23;
}
let x = new A();
class B extends A { 
  getValue() { return this.value; }
}
let b = new B();
Object.setPrototypeOf(B.prototype, x);
console.log(b.getValue());  // prints 23

So, in TypeScript protected does not make any sense to share a secret, too.

Therefore, only the friend class concept makes sense to share secrets between classes but I suppose that an implementation is difficult, not realizable, or not worth the effort.

1 Like

So, while I don't see it being possible for the "protected" to work in javascript, I'm actually not as sure about "friends", maybe others would know more.

But, the link you shared in your original post to this proposal on private declarations seems to have the potential to provide similar functionality that "friend classes" would provide. One could declare the class's shared private fields at the module level and share them with other classes in the same module, or, if the additional idea goes through to allow the ability to share these private fields between modules, one could move these classes to different modules, and they would effectively be friends. No one else could access that shared private data except those who you shared these private fields with.

I am always confusing that JavaScript how to ensure that variables defined with private identifiers? use WeakMap to store them?

I guessed that using Symbols like this:

class A { constructor() { this[Symbol('#private')] = 'unreachable value?' } }

But I finally found that we could still get the key via Object.getOwnPropertySymbols:

const a = new A();
a[Object.getOwnPropertySymbols(a)[0]]; // => "unreachable value?"

The semantics are identical to a closed-over WeakMap per field, with the key as the instance.

Like this?

const privates = new WeakMap();
module.exports = class A {
    constructor() { const p = 'private'; privates.set(this, {p}); }
    method() {
        privates.get(this).p // => private
    }
};

I think of it as one map per field rather than one per instance, but essentially yes (if you add in the throwing semantics if !privates.has(this))

1 Like

Yesterday, it was already very late here in Germany, and maybe I didn't remember the real meaning of protected in OOP languages. Today, I thought once again about protected class member access, and came to the following results:

  1. Though in Java classes are more static, and the mentioned examples for Javascript and TypeScript do not work in Java, I remember that in Java there exists a kind of reflections. I did not use reflection in Java until now, but I believe that it is possible to access protected class members in Java by using reflrection;
  2. Even in C++ protected class members are not really protected from being accessed. The same even is the case for private class members. An attacker only needs to know the memory layout of a C++ class to read that information;

Therefore, what is really the meaning of protected in OOP languages?

  1. It doesn't mean that a class is protected against a malicious attacker who wants to get access to the data or methods:
  2. OOP uses the concept of a public interface which can be applied by every end user of the class. OOP has also the concept of an implementation part of a class which ought not be applied by an end user of the class. To protect end users from accidentally applying an implementation part of a class, that part is marked by protected or private such that the compiler or interpreter can error on such attempts of usage. The idea behind this concepts is that the implementation part can change but the public part of a class should keep the same. Therefore, an end user should not get access to the implementation part. However, there are cases where subclasses want to use parts of the implementation of the base class. E.g. the subclass is written by the same author, and he wants to share implementation code or data between both classes. As the author knows the meaning of the implementation part, it is no problem. Exactly that is the use case of protected. Furthermore, an end user needs not to know anything about the implementation part which is an advantage in complex systems where the main difficulty is to master the complexity.

What is the conclusion of that all?

  1. Protected makes sense as the meaning is not to protect the access to malicious attackers but to prevent accidental access of end users;
  2. Implementation of a kind of protected access modifier in Javascript is still an open feature request;

Can You give me please any link to the real algorithms used to resolve access to private class members? I still think that there should be an efficient way to implement protected class members in Javascript, and how I mentioned in my last post for @theScottyJam, protected access modifiers make sense in Javascript. Please read that post.

Maybe one thing I'm getting a bit hung up on is that we already had a way to do "private" in javascript without the runtime helping out. We just use the commonly-known naming convention of prefixing our private variable names with "_". However, the committee decided that wasn't good enough, and they wanted a way to make something provably private (even more so than private-ness in Java, assuming that can be accessed by reflection too, I've never used their reflection API).

If protectedness was a common need, and we didn't care so much about enforcing access, we could also use a naming convention that implies a value is protected. Python encourages this - they encourage using two underscores for private and one for protected when you expect your class to be inherited from. Some other examples a project could adopt:

  • Two leading underscores. Or, just one leading underscore as we now have a real way to do private without using leading underscores.
  • Putting all protected members in a public object on the class, called "protectedAPI", and just expecting users to know not to touch it if they're not inheriting from it.

Linter rules could be configured to enforce this project-wide naming convention too.

Because there really isn't a great way to enforce protectedness in Javascript anyways, it doesn't seem like an explicitly protected feature is much of a step up over a naming convention. At least with Java, protectedness only leaked through explicit API's they exposed. In Javascript, it'll be leaking simply because we aren't capable of plugging up all of the holes.

I've read years ago to not do that because it's no guarantee for anything, as junior developers might not be aware of this while senior developers might think they know enough to risk using it.

Before #properties, it was reasonable to simply assume that everything on the object is public. Period. You wanted private, you'd go with closures.

(This reply originally included a rant about why javascript is not just a "message passing language", but I removed it - I don't want to get into a big debate over this - no one's going to change anyone's mind, so it doesn't matter)

That certainly is another valid class of thought. I will admit, that a simple naming convention is really week. Anything that adds even a little more protection than that would prevent a lot of wrong usage.

Using a symbol is an easy way to make a public property that it’s very difficult for someone to accidentally interact with, no naming convention is needed.

3 Likes

When it comes to Symbol, is it possible to make @ valid in naming a variable, and use @@ as syntactic sugar for Symbol like: @@key for Symbol('key')

I'm not sure how that would be an improvement, and it would conceptually conflict with decorators.

It seems there is no situations of double @ in the proposal around decorators: GitHub - tc39/proposal-decorators: Decorators for ES6 classes

The way protected or friend fields work with symbols is basically as follows:

const friendApi = Symbol('Friend API')
class MainClass {
  [friendApi] = { ... }
  ...
}
class AnotherClass {
  constructor(instanceOfMainClass) {
    instanceOfMainClass[friendApi].doSomething()
  }
}

Whoever has access to the friendApi symbol has access to MainClass's friend API. I don't really see how a "@@" shorthand really improve this that much. You're basically just turning the first line from const friendApi = Symbol('Friend API') to const friendApi = @@FriendAPI.

Maybe you're getting confused and thinking that you would be able to remove that declaration on the first line, and just use @@friendAPI in the two places where we're using a symbol - but remember that Symbol('Friend API') !== Symbol('Friend API').

I'm just meaning that use @@ as a shortcut for creating a new Symbol, which means that @@x !== @@x still assert true in this situation. The main disadvantage I think is that such a short cut may not be proper for the situation where we need a key with empty character: @@a_b for Symbol('a b').

Smalltalk– which JS is an inspiration of– has public and private, and nothing else.

1 Like