Sitemap

JavaScript “protected” properties

3 min readOct 14, 2025

Private JS fields are both cool and “wasteful”, let me expand on that.

class A {
#value;
constructor(value) {
this.#value = value;
}
}

class B extends A {
#value;
constructor(value) {
super();
this.#value = value;
}
}

If we create new B('secret') the instance will have 2 private fields:

  • the #value that is defined through the A class, a reference that only A class definition can reach/change/read/use and that will throw if anything in B tries to reach it, as in super.#value — nope nopity nope, that will be a SyntaxError: Unexpected private field
  • the #value that is defined through the B class, a reference that only B class definition can reach/change/read/use

The “wasteful” part of this specification can be described as such:

  • we need accessors to eventually be able to at least read that super value but accessors fully invalidate the secret/private nature of that field, if the reason to expose these is to have a way for the inherited class to deal with its inherited internals
  • the field needs to use “two private slots” instead of one, something that the protected keyword would’ve solved, something that does not exist (yet?) in the JavaScript Programming Language
// ⚠️ this does not exist/work

class A {
// @protected meta example
&value;
constructor(value) {
this.&value = value;
}
}

class B extends A {
log() {
console.log(this.&value);
}
}

new B('protected').log();
// 'protected'

A workaround for protected fields

Because only at class definition time we can access private fields, what if we wrap such access in a way that is still “private”, at least per module scope, and grants either read or write access?

// 🥳 this works wonderfully
class A {
// #value read/write
static value(self, ..._) {
if (_.length) [self.#value] = _;
return self.#value;
}

#value;

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

// extract the static method and ...
const { value } = A;
// ... erase the static method !!!
delete A.value;


// ... there we go ...
class B extends A {
log() {
console.log(value(this));
// change value via
// value(this, 'change')
}
}

new B('secret').log();
// 'secret'

… plus a quick helper …

Because forgetting to remove the static field might happen and because this pattern works also for private methods, wouldn’t be cool to use a tiny helper that helps us extracting such properties/fields?

const _protected = Class => new Proxy(Class, {
get(Class, staticField) {
const value = Class[staticField];
delete Class[staticField];
return value;
}
});

const { value, method } = _protected(A);

Update: an even simpler approach 🥳

Huge thanks @tombl_ for pointing out there’s not even a need to delete the static field because a static block would work the same:

let value;
class A {
static {
// #value read/write
value = (self, ..._) => {
if (_.length) [self.#value] = _;
return self.#value;
};
}

#value;

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

class B extends A {
log() {
console.log(value(this));
}
}

new B('secret').log();
// 'secret'

Gotta admit I often forgot about the static block in classes, this makes the helper obsolete already, which is great!

And “that’s all folks”, if you ever need to expose internally or within a private scope private fields, now you know a little trick to do so!

Enjoy JS 👋

--

--

Andrea Giammarchi
Andrea Giammarchi

Written by Andrea Giammarchi

Web, Mobile, IoT, and all JS things since 00's. Formerly JS engineer at @nokia, @facebook, @twitter.

No responses yet