Javascript (ES8) - Get static value of class from parent class - javascript

I have a class Parent (defined using the class declaration, though I'm aware this is primarily syntactic sugar), and multiple classes (Child1, Child2, etc) that extend it. The child classes have a static property assigned to them (outside of their declarations - as far as I'm aware there is no way to assign a static property within a class declaration).
I would like to access the a static value of any child class from the parent class, such as in the method getStaticValue().
class Parent {
constructor() {
//Do parent stuff
}
getStaticValue() {
return "The value of staticValue is " + this.staticValue;
}
}
class Child1 extends Parent {
constructor() {
super();
//Do child1 stuff
}
}
Child1.staticValue = "Child1";
class Child2 extends Parent {
constructor() {
super();
//Do child2 stuff
}
}
Child2.staticValue = "Child2";
I want to access the value of staticValue of any arbitrary child from the parent class, however attempting to do so as written above always returns undefined. In other words:
let parentStaticValue = new Parent().getStaticValue();
//Desired output = "The value of staticValue is undefined"
//Actual output = "The value of staticValue is undefined"
let child1StaticValue = new Child1().getStaticValue();
//Desired output = "The value of staticValue is Child1"
//Actual output = "The value of staticValue is undefined"
let child2StaticValue = new Child2().getStaticValue();
//Desired output = "The value of staticValue is Child2"
//Actual output = "The value of staticValue is undefined"
Is there a way to access the a static value of a child class from the parent class, without having to know the name of the child class in every case?

You can use the constructor property of a class instance to access static properties held as properties of the instance's constructor function, as in:
class Parent {
constructor() {
//Do parent stuff
}
getStaticValue() {
return this.constructor.staticValue;
}
}
Warning
The constructor property of an object is inherited from the prototype chain.
Class syntax made the prototype object property of a class constructor's function object non-configurable (good), but left the constructor property of this same prototype object writable (bad).
NEVER change the constructor value of class constructors' prototype objects if you know what's good for you want class extensions to work correctly.
Demo:
class Parent {
constructor() {
//Do parent stuff
}
getStaticValue() {
return "The value of staticValue is " + this.constructor.staticValue;
}
}
class Child1 extends Parent {
constructor() {
super();
//Do child1 stuff
}
}
Child1.staticValue = "Child1";
class Child2 extends Parent {
constructor() {
super();
//Do child2 stuff
}
}
Child2.staticValue = "Child2";
console.log(new Parent().getStaticValue());
console.log(new Child1().getStaticValue());
console.log(new Child2().getStaticValue());

You can pass your static value to the parent constructor using super() in a child class:
class Parent {
constructor(childStaticValue) { // receive the value from the children class
this.staticValue = childStaticValue // and assign it to the local variable
}
getStaticValue() {
return "The value of staticValue is " + this.staticValue;
}
}
class Child1 extends Parent {
constructor() {
super(Child1.staticValue); // pass the value to the parent class
//Do child1 stuff
}
}
Child1.staticValue = "Child1";
class Child2 extends Parent {
constructor() {
super(Child2.staticValue); // pass the value to the parent class
//Do child2 stuff
}
}
Child2.staticValue = "Child2";

Related

Extends behaviour in js with default value

I have found interesting behaviour of js extends, and do not understand the reasons of it
in case of copy values right from another value, for some reasons value will be copied from parent
class parent {
defaultValue = 1;
value = this.defaultValue;
}
new parent() // {defaultValue: 1, value: 1}
class child extends parent {
defaultValue = 2;
}
new child() // {defaultValue: 2, value: 1}
which is really not obvious and unclear for me
but if i replace it by function or even getter the behaviour will be changed, and i get the value from child
class parent {
get defaultValue() { return 1; }
value = this.defaultValue;
}
new parent() // {defaultValue: 1, value: 1}
class child extends parent {
get defaultValue() { return 2; }
}
new child() // {defaultValue: 2, value: 2}
the main question here, is why in the moment of child creation in first case JS looking on parent class to take value, but in second case JS looking on child class to take value
Can someone explain the reason of such behaviour?
EDIT See t.niese or Yury Tarabanko answers for details
the short answer seems in next way
getters(also function) and function will be overridden in prototype which allow them to be called by parent with child changes (in real it is expected)
While first example with assignee simple values will be called only in the moment of class creation (constructor or super) and it will be appear only in scope of current class (which cannot be changed by child) and prototype (which can be changed by child)
A related question is: how to access overridden parent class functions in parent class code.
Getters and Setters are functions that are defined with the definition of the class, so in the constructor of the parent class (and the initiation of its instance class fields) you could call a function that only exists in child (which indeed might be a bit strange):
class parent {
value = this.test();
constructor() {
this.test()
}
}
class child extends parent {
test() {
console.log('test')
}
}
new child()
So which function (or getter/setter) is called is already defined with the class definition, before the instancing is done.
Public instance class fields on the other hand are initialized/set during initialization phase of an instance in an particular order (the shown code might only work in chrome based browsers):
class parent {
defaultValue = (() => {
console.log('parent:init defaultValue')
return 1;
})();
value = (() => {
console.log('parent:init value')
return this.defaultValue;
})();
constructor() {
console.log('parent constructor')
}
}
class child extends parent {
defaultValue = (() => {
console.log('child:init defaultValue')
return 2;
})();
constructor() {
console.log('child constructor before super()')
super()
console.log('child constructor after super()')
}
}
new child()
In your first example, the creation and initialization of the public instance field named defaultValue in Child occurs after the creation and initialization of the public instance field named value in Parent.
So: even though the this value in the initializer of the public instance field named value in Parent will point to the instance of Child under construction, the child-local public instance field named defaultValue does not yet exist, and so the prototype chain is followed up to the property named defaultValue on the instance of Parent, yielding 1.
In your latter example, you have getter functions named defaultValue.
Getter functions specified in this way, even though their API deliberately looks like that of public instance fields, will end-up as functions on the [[Prototype]] of any instance under construction.
The [[Prototype]] objects of instances are created at the time of class declaration (ie. always before anything triggered by instance construction), as the .prototype property of the class (or constructor function); references to these objects are then copied to the [[Prototype]] of any instance under construction as the first step in object construction (think Object.create(class.prototype)).
And so this.defaultValue from the Parent public instance initializer for value resolves to the getter on the [[Prototype]] of the instance of Child under construction, which is a function that returns 2.
It is happening because getters are defined on prototypes while instance properties are defined on instance (as the name imply)
So, when Child1 instance is created it first defines properties from Parent1 and you get defaultValue = 1
On contrary when Child2 instance is created the Child2.prototype will already have property defaultValue overriden.
class Parent1 {
defaultValue = 1;
value = this.defaultValue;
}
class Child1 extends Parent1 {
defaultValue = 2;
}
class Parent2 {
get defaultValue() { return 1; }
value = this.defaultValue;
}
class Child2 extends Parent2 {
get defaultValue() { return 2; }
}
console.log(Object.hasOwn(new Child1(), 'defaultValue'))
console.log(Object.hasOwn(new Child2(), 'defaultValue'))

Accessing private properties in derived class

I am having a parent class with a private property with a setter function. In my derived class I want to override the setter function and additionally I want access a private variable from within the derived class:
class Mother
{
#name
constructor()
{
this.name = "Paula";
}
set name(aName)
{
this.#name = aName;
}
get name()
{
return this.#name;
}
}
class Child extends Mother
{
#tattooText
constructor()
{
super();
this.#tattooText = null;
}
set tattooText(value)
{
this.#tattooText = value;
}
get tattooText()
{
return this.#tattooText;
}
set name(aName)
{
super.name = aName;
if (this.#tattooText === null)
this.#tattooText = aName;
}
get name()
{
return super.name;
}
}
Unfortunatelly, this will not work, because I dont have access to the private #tattooText property from within the derived class. Seems like the overwritten "set name" function is getting executed in the scope of the parent class.
Do you have any idea how to solve this task? How can i get access to private properties of a derived class from within an overwritten setter function?
Regards
Robert
Edited: The problem only appears, when I try to "prefill" the name property from within a constructor in the Mother-Class. (see edited source)

Parent class constructor can't access child fields without Timeout

I am making an application using JavaScript. One thing that I noticed is that when I call super() from child class constructor, I cannot use child class fields.
For example:
class Parent {
constructor() {
console.log(this.x);
}
}
class Child extends Parent {
x = 5;
constructor() {
super();
}
}
Output: undefined
As you see, the parent constructor cannot access the x field of the child constructor. There are similar questions on stackoverflow like:
Access JavaScript class property in parent class
The solution they give is to use an init() method on the Parent class. I realized that every method other than the constructor of the Parent class has access to child fields.
class Parent {
init() {
console.log(this.x);
}
}
class Child extends Parent {
x = 5;
constructor() {
super();
this.init();
}
}
Output: 5
However, I can also get access to the x field by using Timeout with delay of 0:
class Parent {
constructor() {
setTimout(() => console.log(this.x), 0);
}
}
class Child extends Parent {
x = 5;
constructor() {
super();
}
}
Output: 5
All the answers on stackoverflow give the solution, but nobody explains how and why this happens. So my question is:
1. Why the constructor doesn't have access to fields of the child class?
2. Why adding a timeout function gives access to the fields of the child class?

Cannot understand class to function mapping in javascript

class Parent {
print() {
console.log('hey i am parent');
}
}
class Child extends Parent {
constructor() {
super();
}
print() {
console.log('hey i am child');
}
}
x = new Parent();
console.log(Object.getPrototypeOf(x))
x.print();
Though the [[prototype]] of x is an empty object but still It can access the print() function which is defined in class Parent.
I cannot understand why Object.getPrototypeOf(x) is an empty object.
It's in there, just non-enumerable. Try this:
Object.getOwnPropertyNames(Object.getPrototypeOf(x));
// ["constructor", "print"]
class Parent {
print() {
console.log('hey i am parent');
}
}
class Child extends Parent {
constructor() {
super();
}
print() {
console.log('hey i am child');
}
}
x = new Parent();
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(x)));
x.print();
What makes you think it's empty? It does have constructor and print properties, they are however not enumerable and not displayed by default on the console. (And of course it does have a [[prototype]] link to Object.prototype, but how/whether that is displayed depends on your console as well).
To inspect them, have a look at Get functions (methods) of a class.

Is there a way to discover if a javascript 6 class defines its own constructor?

Is there any way to determine if a subclass implements a constructor from within a static method (in a base class)?
I'm trying to write a static create method (that acts like the new keyword) that by default works by passing attribute values as a properties object:
class Person extends Class {
greet() { return 'hello from ' + this.name; }
}
var p = Person.create({name: 'world'}; // create a new Person object and set its `name` property to `'world'`
console.log(p.greet()); // => "hello from world"
but hands off to the class' constructor if it has one:
class Person2 extends Class {
constructor(name) {
super();
this.name = name;
}
greet() { return 'hello from ' + this.name; }
}
var p = Person2.create('world');
console.log(p.greet()); // => "hello from world"
I'm stuck at finding out if the subclass defines its own constructor..
class Class {
static create(...args) {
let has_ctor = ?? // true iff the current subclass defines a constructor..
if (has_ctor) {
// let the constructor handle everything
return new this(...args);
} else {
// assume that `args` contains exactly 1 pojo that defines instance variables to be overridden..
var instance = new this();
let props = args[0];
for (let prop in props) instance[prop] = props[prop];
return instance;
}
}
}
is this even possible?
Seems like it would be much easier to do
class Class {
static create(...args) {
// let the constructor handle everything
return new this(...args);
}
constructor(props){
Object.assign(this, props);
}
}
then if things override the constructor, then can choose to pass props to super() or to assign them manually themselves.
Just to answer your original question
Is there a way to discover if a javascript 6 class defines its own constructor?
No, there is not. Every class does have its own constructor, because a "class" basically is just the constructor function.
If a class definition does not include a constructor method, then it is automatically supplied by the language (see ยง14.5.14); either as
constructor(...args){ super (...args);}
if there is a super class or as
constructor(){ }
if there is none. The result is not distinguishable from a class where such a constructor was explicitly declared.

Categories