2

I'd like to access private fields of base class from derived classes without making them public (what is called 'protected' in other languages).

Consider the following class:

class Animal {

  #privateProp;

  constructor() {

  this.#privateProp = 12;

  }
}

Now the extending class:

class Cat extends Animal {

  constructor() {

    super();
  }

  doIt() {

    console.log(this.#privateProp) // 1 below
    console.log(super.#privateProp) // 2 below
  }
}

I'd like to execute as if it was protected:

new Cat().doIt();

But gets (respectively):

  1. Uncaught SyntaxError: Private field '#privateProp' must be declared in an enclosing class
  2. Uncaught SyntaxError: Unexpected private field

Notice that this code would work perfectly when privateProp becomes public, But I want to achieve a protected like behavior and get access to the 'private' fields like any language that support inheritance.

Any help will be appreciated.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Tamir Nakar
  • 933
  • 1
  • 10
  • 17

2 Answers2

1

Fields are private in a similar way to how variables are block-scoped; if a property is private to a certain class, it may only be referenced inside that class. If you extend the class, it won't be visible in the derived class.

You could make a getter/setter on the superclass, if you want to be able to do stuff with it from the subclass:

class Animal {
  #privateProp = 12;
  setProp(val) {
    this.#privateProp = val;
  }
  getProp() {
    return this.#privateProp;
  }
}
class Cat extends Animal {
  doIt() {
    console.log(this.getProp());
  }
}
new Cat().doIt();

Another way is to define the "private" field as a WeakMap scoped only to the class declarations instead:

const { Animal, Cat } = (() => {
  const privateProps = new WeakMap();
  class Animal {
    constructor() {
      privateProps.set(this, 12);
    }
  }
  class Cat extends Animal {
    doIt() {
      console.log(privateProps.get(this));
    }
  }
  return { Animal, Cat };
})();
new Cat().doIt();
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • can also create an actual getter and setter methods i.e. `get privateProp(){}` – AZ_ Apr 15 '20 at 09:30
  • Didn't it make #privateProp to be effectively public? so anyone can go new Animal().getProp() ? – Tamir Nakar Apr 15 '20 at 09:30
  • @TamirNakar The only way for a subclass to interact with a superclass's private field is for the superclass to expose functionality allowing for interaction in some way. – CertainPerformance Apr 15 '20 at 09:32
  • @CertainPerformance, Agreed. But i'd like to allow as less interaction as possibe. In java for instane you define the field as protected and only derived classes can get access to this prop. Can this behavior be achieved? – Tamir Nakar Apr 15 '20 at 09:34
  • 1
    @TamirNakar With the current syntax rules, not really, unfortunately. If it were me, in this situation, I'd make a WeakMap scoped only to the classes instead – CertainPerformance Apr 15 '20 at 09:44
1

you can create a private property with getter and setter methods having restricted access by checking if the constructor is not of the parent class itself.

class Animal {
  #privateProp = 12;
  set Prop(val) {
    if (this.constructor.name !== 'Animal')
      return this.#privateProp = val;
    throw new Error('Cannot Access Protected property');
  }
  get Prop() {
    if (this.constructor.name !== 'Animal')
      return this.#privateProp;
    throw new Error('Cannot Access Protected property');
  }
}

class Cat extends Animal {
  get Prop() {
    return super.Prop;
  }

  set Prop(val) {
    super.Prop = val
  }
}

let cat = new Cat();
console.log(cat.Prop)
cat.Prop = 22
console.log(cat.Prop)

console.log(new Animal().Prop);
AZ_
  • 3,094
  • 1
  • 9
  • 19
  • Beautiful! Thank you – Tamir Nakar Apr 15 '20 at 21:20
  • This is no longer a private (or protected) property if you provide public getters and setters. – Bergi Jul 20 '20 at 19:53
  • @Bergi they are not the protected variables but a workaround to achieve that, here only parent or child classes can access the variables, not any other class which actually is protected variable behavior. if we need to access these variables in other classes we can implement another getter and setters wrappers in the parent class. – AZ_ Jul 22 '20 at 10:05
  • @AZ_ No. *Everyone* can access these properties on subclass instances from *anywhere*, as your code (`console.log(cat.Prop); cat.Prop = 22`) demonstrates. It's not restricted to the code inside child classes. – Bergi Jul 22 '20 at 10:12
  • @Bergi that way you are not accessing protected property `Prop` of the parent, but the public property `Prop` of the child. I just kept it as it was in the parent. In the child, OP can choose not to have getter and setter exactly returning the parent prop. it could be `return super.Prop+1`; or maybe use only in a method. – AZ_ Jul 22 '20 at 10:20
  • @AZ_ If the child didn't override it, the inherited getters/setters would still be available. And even if they were overridden, one could use the getters/setters from the parent on the child instance (e.g. with `Reflect.get(Animal.prototype, "Prop", cat)`) – Bergi Jul 22 '20 at 10:29
  • ahh got it. @Bergi – AZ_ Jul 22 '20 at 10:42