Is there a more direct way to achieve this in Javascript? - javascript

I have an inheritance like this:
class Parent {
some_object_property = ["some_object_property_depending_on_initialization"];
constructor() {...}
}
class Child extends Parent {
some_object_property = [...super.some_object_property, 'child_property']
}
Is there a way to let the Child inherit and extend object property some_object_property from the Parent?
I know I can achieve the end result through getter and setter. Is there a more direct way?

Did you mean this?
class Parent {
constructor() {
this.someProp = ['someprop test', 'something else'];
}
}
class Child extends Parent {
constructor() {
super();
this.someProp = [...this.someProp ,'added child prop'];
}
}
let myChild = new Child();
console.log(myChild.someProp);

It produces an error saying "super is not an identifier recognizable".
Yes, you cannot use super in a class field initialiser. Either put that line inside the constructor where it belongs (like in #DannyEbbers' answer), or just use the this keyword instead - it's just as good, no need to use super here:
class Parent {
some_object_property = ["some_object_property_depending_on_initialization"];
}
class Child extends Parent {
some_object_property = [...this.some_object_property, 'child_property']
}
this.some_object_property will refer to the instance property that was initialised by the parent constructor before you're overwriting it with a new value - it runs as if written inside the constructor.

Related

Inability to set value of public class field from constructor of super if that field is redefined in the inheriting class

I use polymorphism with JSDoc to describe concrete implementations of some classes. Up until now I have been doing this with getters and setters; newly implemented public class fields can shorten this getter and setter boilerplate considerably.
My problem is described by the code snippet below. The value set in the constructor does not survive the redefining of the public class field. How can I redefine a public class field in an inheriting class and keep the value set in the super constructor? Is this a bug in public class fields?
class ThingHolder {
/**#type {notKnownYet}*/
publicFieldForThing
constructor(thing) {
this.publicFieldForThing = thing
//Do stuff for all thing holders
}
dropMyThing() {
//Throw not implemented
}
setThingField(thing) {
this.publicFieldForThing = thing
}
}
class RedThingHolder extends ThingHolder {
/**#type {RedThing}*/
publicFieldForThing
dropMyThing() {
//functionality for dropping a red thing with JSDoc niceties for RedThings
}
}
class BlueThingHolder extends ThingHolder {
/**#type {BlueThing}*/
publicFieldForThing
dropMyThing() {
//functionality for dropping a blue thing with JSDoc niceties for BlueThings
}
}
const redThing = {
colour: "red"
}
//Setting the thing in the constructor results in unextected behaviour
const redThingHolder = new RedThingHolder(redThing)
console.assert(redThingHolder.publicFieldForThing === redThing)
//Setting the thing through an inherited method after construction works perfectly fine
redThingHolder.setThingField(redThing)
console.assert(redThingHolder.publicFieldForThing === redThing)
Edit: This question was not about my motivations. Why do public fields behave differently to getters with regard to the prototype chain?
class Parent {
constructor() {
console.log("prop in instance during super: ", "prop" in this)
console.log("publicField in instance during super: ", "publicField" in this)
}
}
class Child extends Parent {
constructor() {
super()
console.log("prop in instance after super: ", "prop" in this)
console.log("publicField in instance after super: ", "publicField" in this)
}
publicField
get prop() { }
}
new Child
When you call the constructor of RedThingHolder class
new RedThingHolder(redThing)
the default constructor of RedThingHolder will pass the parameter redThing to the super class' constructor which will add a property publicFieldForThing on the newly created object.
Re-definition of publicFieldForThing in child classes overwrites the publicFieldForThing set inside the super class.
I feel like this is a bug and that the value should be retained
automatically.
Its not a bug and redefining a property with the same name will not retain a value because you are defining a field with a name that already exists on an object. Instead of retaining, it will overwrite the existing field.
Doing this
class RedThingHolder {
publicFieldForThing;
...
}
will overwrite the publicFieldForThing field with the default value of undefined.
How can I redefine a public class field in an inheriting class and
keep the value set in the super constructor?
You can set the publicFieldForThing in the child class constructor using the getter for publicFieldForThing defined in the super class.
class ThingHolder {
constructor(thing) {
this.publicFieldForThing = thing;
}
get publicField() {
return this.publicFieldForThing;
}
}
class RedThingHolder extends ThingHolder {
constructor(thing) {
super(thing);
this.publicFieldForThing = this.publicField;
}
}
const redThing = {
colour: 'red',
};
const redThingHolder = new RedThingHolder(redThing);
console.log(redThingHolder.publicFieldForThing === redThing);
Edit
Why do public fields behave differently to getters with regard to the
prototype chain?
getter/setters in ES2015's classes are added on the proptotype object whereas the public fields are added on the object itself.
In the code example you posted, prop is added on the Child.prototype whereas the publicField is like doing
this.publicField = undefined;
in the constructor of the Child class just after the super() call.
This is why "prop" in this inside the constructor of the Parent class returns true whereas "publicField" in this evaluates to true only inside the constructor of the Child class.

JavaScript - Accessing a child variable in a parent class

In JavasScript, is it possible to define a variable in a child class, then access it in a parent class? I imagine the child class would look something like this:
export default class ChildClass extends ParentClass {
constructor() {
this.path = 'register';
}
}
A use-case scenario is an HTTP service class as the parent that contains general, reusable methods and a child class that defines the exact route that will be accessed. Assuming it's possible, how could I then access this.path in the parent class?
You don't need to do anything special, it just works.
As mentioned in the comments, it doesn't necessarily make sense to design your classes like this, since the actual class of the object might not be that child class.
class ParentClass {
printPath() {
console.log(this.path);
}
}
class ChildClass extends ParentClass {
constructor() {
super();
this.path = 'register';
}
}
var c = new ChildClass();
c.printPath();
var p = new ParentClass();
p.printPath();

Javascript Child Class method not overriding Parent Class Method

I am trying to override one method from the parent class, but there are some issues.
Below is the code snippet of my scenario which I am trying.
class Parent {
add = () => {
console.log('Parent method');
}
}
class Child extends Parent {
add () {
console.log('Child Method');
}
}
// Creating an instance
const child = new Child();
child.add();
It is calling the Parent method add as that is arrow function, Can someone explain why this is happening. If I make the parent function a simple javascript method then child is able to override.
Additonal Details :
I don't have access to Parent as it is part of library.
I can't make my child class method as instance properties (arrow function)
, the reason for being that there are further
specification written for child (child of child) and If we use arrow
functions we will not be able to call the super.
Child function name can't be renamed.
This is one of few reasons why arrow methods aren't convenient. They limit the ways in which a class can be extended and tested.
Class fields (which arrow methods are) are syntactic sugar for constructor code:
class Parent {
constructor() {
this.add = () => {...};
}
}
Only another arrow method can override parent arrow method, because they are defined in class constructor, not on class prototype:
class Child extends Parent {
add = () => {
/* no super.add here because add is not prototype method */
}
}
If super.add is intended to be used, a workaround is to store parent method:
class Child extends Parent {
superAdd = this.add;
add = () => {
this.superAdd();
}
}
Notice that since this is syntactic sugar for constructor code, the order in which superAdd and add are defined matters.
The parent add is an instance property, and it overshadows the child's class method, which is part of the instance's prototype. It's a bit hacking, but you can rename and delete the class property in the constructor:
class Parent {
add = () => {
console.log('Parent method');
}
}
class Child extends Parent {
constructor() {
super();
this.parentAdd = this.add;
delete this.add;
}
add() {
console.log('Child Method');
this.parentAdd(); // if you need call the parent's method
}
}
const child = new Child();
child.add();

How to implement singleton pattern in the following situation?

const instance = Symbol('instance');
class Parent {
number;
constructor() {
const Class = this.constructor;
if (!Class[instance]) Class[instance] = this;
return Class[instance];
}
set_number (number) {
this.number = number;
}
}
class Child extends Parent {
test () { }
}
class Child2 extends Parent {
test () { }
}
const child = new Child();
const parent = new Parent();
const child2 = new Child2();
child.set_number(1);
child.test();
child2.set_number(2);
child2.test(); // child2.test is not a function
parent.set_number(3);
console.log(child.number); // output : 1
console.log(child2.number); // output : 3 (I want it to be 2.)
console.log(parent.number); // output : 3
I implmented Singleton by assigning instances to the constructor.
I want to be implemented in singleton for each of the three classes Parent, Child, and Child2. But there is a problem, when I instantiate Parent class before other classes.
I tried to come up with good idea, but fail. Is there any way I can solve it?
test URL
Class[instance] looks up instance throughout the prototype (inheritance) chain. So in case of Child2 it starts with Child2, and if there is no property it continues with Parent, then with Object. The result is that you return an instance of Parent when you want create the instance of Child2.
Instead of checking the existence of the property by using a lookup, you should use hasOwnProperty():
if (!Class.hasOwnProperty(instance)) Class[instance] = this;
That means if Child2 itself(!) does not have an instance, then assign one to it.

Javascript classes : how to access overridden parent class functions in parent class code

Javascript ES6 ( node 8.4.0 and latest chrome and recent Firefox )
I expected
class Parent {
init(){
console.log("Parent init") ;
this._surname = "McClass" ;
}
constructor() {
console.log("Parent constructor") ;
this.init();
}
get surname(){
return this._surname ;
}
}
class Child extends Parent {
init(){
console.log("Child init") ;
}
constructor() {
super();
console.log("Child constructor") ;
this.init();
}
}
var child = new Child() ;
console.log(child.surname);
to give the following output;
Parent constructor
Parent init
Child constructor
Child init
McClass
(which is what comparable C++ code gives)
Alas, I got this ;
Parent constructor
Child init
Child constructor
Child init
undefined
Am I doing something wrong or is this the correct intended behaviour and if so how is it justified ?
EDIT;
See MinusFour's answer below on how to achieve what I was trying to do / expecting.
As to why the observed output is the "correct" behaviour and justified ;
As Bergi pointed out (in comments) all calls to object methods in js are effectively "virtual" (the last method of that name added to the object's prototype inheritance chain being the first found and hence executed). It turns out calls are still effectively virtual in a class construction context.
C++ does not apply virtual method behaviour during construction but then again Java does and you get the same output (as above) in comparable Java code so there is a precedent for the observed behaviour.
You could do:
Parent.prototype.init.call(this);
class Parent {
init() {
console.log("Parent init");
this._surname = "McClass";
}
constructor() {
console.log("Parent constructor");
Parent.prototype.init.call(this);
}
get surname() {
return this._surname;
}
}
class Child extends Parent {
init() {
console.log("Child init");
}
constructor() {
super();
console.log("Child constructor");
this.init();
}
}
var child = new Child();
To make sure it never gets overridden, but I would suggest you just not override it in the first place.
It is expected behaviour, just because it can be seen in established ES6 class implementations that follow the specs.
this refers to current class instance, which is an instance of Child in the case when Child is instantiated - even in Parent class, because there is only one instance, and it is instanceof Child.
If Child overrides the method, it's its responsibility to provide mechanism to call it. Considering that init follows some documented convention and is the place where class initialization happens in order to make constructor leaner and more testable, it is:
class Parent {
init(){...}
constructor() {
this.init();
}
...
}
...
class Child extends Parent {
init(){
super.init();
...
}
// optional, as long as `init` contains all init logic
constructor() {
super();
}
}
Which results in a sequence:
Parent constructor
Parent init
Child init
Child constructor
If init is supposed to work totally independently in both classes, it shouldn't be overridden. Methods should be named differently, like initParent and initChild. Or any other way to avoid naming collisions can be used, e.g.:
const init = Symbol('Parent init');
class Parent {
[init](){...}
constructor() {
this[init]();
}
...
}
...
const init = Symbol('Child init');
class Child extends Parent {
[init](){...}
constructor() {
this[init](); // totally independent method
}
}

Categories

Resources