3

What is the best way to simulate protected variables in javascript?

class Parent{

    #variable; // Private variable. This is not to be public.

    constructor(value)
    {
        this.#variable = value;
    }
}

class Child extends Parent{

    getNewVariable()
    {
        return (1 + this.#variable); // Uncaught SyntaxError: Private field '#variable' must be declared in an enclosing class
                                     // We want #variable to be accessible without making it public.
    }
}

Since javascript doesn't support protected variables yet,

What is the best option for accessing private variables from superclasses in extended classes?

I haven't been able to find much documentation on this, so any suggestions would be very helpful.

4
  • What about getter and setter? Commented Sep 23, 2021 at 11:06
  • Hi @ClausBönnhoff: Thanks for the suggestion. Getters and setters definitely work, but I was hoping to cut down on syntactic sugar: Writing a get variable(){ return this.#variable; } For each variable is not ideal. However if this is the only option, then it will have to do. :) Commented Sep 23, 2021 at 11:10
  • 1
    use typescript. Commented Sep 23, 2021 at 11:13
  • A protected property is essentially public since everyone could extend the class. So just make it public in the first place. Do not try to simulate a different language, write idiomatic JavaScript instead. Commented Sep 23, 2021 at 12:03

3 Answers 3

2

+++ late answer, but here we go ... +++

A WeakMap based approach most probably covers what the OP is looking for ...

const protectedStatesMap = new WeakMap;

class Parent{
  constructor(foo) {
    protectedStatesMap.set(this, { foo });
  }
  getFoo() {
    return protectedStatesMap.get(this).foo;
  }
}
class Child extends Parent{
  constructor(foo, bar) {
    super(foo);

    Object.assign(protectedStatesMap.get(this), { bar })
  }
  getBar() {
    return protectedStatesMap.get(this).bar;
  }
}
const childA = new Child('foo', 'bar');
const childB = new Child('baz', 'biz');

console.log('childA.getFoo() ...', childA.getFoo());
console.log('childA.getBar() ...', childA.getBar());

console.log('childB.getFoo() ...', childB.getFoo());
console.log('childB.getBar() ...', childB.getBar());
.as-console-wrapper { min-height: 100%!important; top: 0; }

Sign up to request clarification or add additional context in comments.

4 Comments

@AndrewKhassapov ... check out the above solution of this late answer.
Thank you @PeterSeliger!
This is really clever!
The primary issue with this approach is the child class must be defined in the same module as the parent class. So, it's not really a general solution.
1

If you have complete control over all child classes, then Peter's response is probably the way to go. But if you expect your base class to be used in other projects, that won't work.

I find using Symbols can be a pretty good way of simulating protected properties. It's better than just using an underscore prefix on a public property, but it won't actually stop anyone from accessing the properties if they want to jump through hoops. It will protect against naive accidental access.

First, define your base class and a symbol that will be used by inheriting classes.

export const extend = Symbol("extend Base");

export class Base {
    constructor(value) {
        this.value = value;
        this[extend] = {default: value};
    }

    reset() {
        this.value = this[extend].default;
    }
}

Then, define a child class, using the symbol for accessing the properties.

import {Base, extend} from "./base.js"

export class Child extends Base {
    constructor(value) {
        super(value);
    }

    changeDefault(value) {
        this[extend].default = value;
    }
}

This approach keeps the protected properties out of any serialization (JSON.stringify()), keeps them from being overwritten by a simple Object.assign, and serves as an indication to the users of the API that this is not a normal property.

As long as you're not relying on this to truly restrict access, it should serve most needs.

Comments

-1

What about using TypeScript? TypeScript classes support the protected keyword (see docs), and has alot of extra benefits over vanilla JavaScript.

1 Comment

TypeScript supports the syntax but properties marked as private or protected are still fully accessible

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.