In this post, I present an interesting thing I found with JavaScript Chained Extended classes.
Introduction
While working with JavaScript class inheritance, I found something Interesting I would love to share. A way to access private class fields from classes that didn't define them, only extended them, while still keeping them private to the outside world.
Bypass errors like:
Uncaught SyntaxError: Private field '#active' must be declared in an enclosing class
Chained Extended Classes
Below is an example of creating chained multi inheritance of JavaScript classes:
class foo3 {
#active = false;
get active() { return this.#active; }
set active(value) { this.#active = value; }
constructor() { }
$(Class, Name, Value = undefined) {
if (Class!=='foo3') { if (super.$)
{ return super.$(Class, Name, Value); } return; }
switch (Name) {
case '#active':
if (typeof Value === 'undefined') {
return this.#active;
} else {
this.#active = Value;
}
break;
default: if (super.$)
{ return super.$(Class, Name, Value); } break;
}
}
}
class foo2 extends foo3 {
#active = 'no';
get active() { return this.#active; }
set active(value) { this.#active = value; }
constructor() { super(); }
$(Class, Name, Value = undefined) {
if (Class!=='foo2') { if (super.$)
{ return super.$(Class, Name, Value); } return; }
switch (Name) {
case '#active':
if (typeof Value === 'undefined') {
return this.#active;
} else {
this.#active = Value;
}
break;
default: if (super.$)
{ return super.$(Class, Name, Value); } break;
}
}
}
class foo extends foo2 {
constructor() { super(); }
$(Class, Name, Value = undefined) {
throw new Error(`This method is for protected use!`);
}
test() {
super.$('foo2', '#active', 'yes');
super.$('foo3', '#active', true);
const foo2_active = super.$('foo2','#active');
const foo3_active = super.$('foo3','#active');
console.log(`Print Original Values`);
console.log(`Private - foo2.#active = 'no'`);
console.log('Private - foo3.#active = false');
console.log(`Public - this.active = 'no'`);
console.log(`\nPrint Changed Fields`);
console.log(`Private - foo2.#active = '${foo2_active}'`);
console.log(`Private - foo3.#active = ${foo3_active}`);
console.log(`Public - this.active = '${this.active}'`);
this.active = 'off';
console.log(`\nReprint after setting: this.active = 'off'`);
console.log(`Private - foo2.#active = '${foo2_active}'`);
console.log(`Private - foo3.#active = ${foo3_active}`);
console.log(`Public - this.active = '${this.active}'`);
}
}
const f = new foo();
f.test();
Super Recursion Method for Private Access
Notice the public
method named '$
' per each class. This method takes 3 params.
$(Class, Name, Value = undefined)
Class
: The string
name for the extended class that you would like to access a private
field. Name
: The name
of the private
field to access. Value
: The value
to set the private
field to.
This special method allows you to get
and set private
fields directly per class.
$(Class, Name, Value = undefined) {
if (Class!=='foo2')
{ if (super.$) return super.$(Class, Name, Value); return; }
switch (Name) {
case '#active':
if (typeof Value === 'undefined') {
return this.#active;
} else {
this.#active = Value;
}
break;
default: if (super.$) return super.$(Class, Name, Value);
}
}
A couple of things you need to change when creating your own class access. The class name and private fields you want to directly access. You must define everything you want to use. There is no access to private
members like this['#active']
. Only access like this.#active
. So you must declare everything you want to use per string
case. You only need to add the private fields from each class.
Console Output from Above Code
Print Original Values
Private - foo2.#active = 'no'
Private - foo3.#active = false
Public - this.active = 'no'
Print Changed Fields
Private - foo2.#active = 'yes'
Private - foo3.#active = true
Public - this.active = 'yes'
Reprint after setting: this.active = 'off'
Private - foo2.#active = 'yes'
Private - foo3.#active = true
Public - this.active = 'off'
Override Extended Private Method
Protected
methods can look like below.
#message = (str) => {
console.log(`foo2:message('${str}')`);
}
I also changed the super recursive method to handle private
functions and remove having to write string class name. Now, just say the name in the signature call.
New Super Recursive $ Method
Added comment to instruct you about function parameters.
$(Class, Member, Value = undefined) {
if (!(Object.getPrototypeOf(this) instanceof Class) && super.$) { return super.$(Class, Member, Value); }
switch (Member) {
case '#active':
if (typeof Value === 'undefined') { return this.#active; } else { this.#active = Value; }
break;
case '#message':
if (typeof Value === 'undefined') { return this.#message; } else { this.#message = Value; }
break;
default: if (super.$) { return super.$(Class, Member, Value); } break;
}
}
Example Call to $ Method
super.$(foo3, '#active', true);
super.$(foo2, '#message', this.#message);
Example 2 Output
foo2:message('Hello from foo2_test()')
Overriding foo2:#message() with foo:message()
foo:message('Hello from foo2_test()')
this.active = no
this.active2 = false
this.active = true
this.active2 = Yes
Uncaught Error: This method is for protected use!
at foo.$ (test2.html:100:15)
at test2.html:131:5
History
- 16th January, 2023: Override Private Function
- 14th January, 2023: Initial version