How to implement singleton pattern in the following situation? - javascript

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.

Related

Is there a more direct way to achieve this in 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.

How to extend a parent class' instance variable in javascript

I am trying to extend a parent class's instance variable but flow js is complaining that this is not correct. Is there something I am missing?
// BaseClass
export type AdType = {
dom: HTMLElement,
};
export default class AdsRefresh {
ads: AdType[] = [];
constructor(configs) {
this.ads = configs;
}
}
// ChildClass
import type {AdType as BaseAdType, PlaceholderType} from './adsRefresh';
export type AdType = {
placeholderIndex?: number
} & BaseAdType;
class AdsRefreshTiler extends AdsRefresh {
ads: AdType[] = [];
constructor(configs) {
super(configs);
this.ads = this.getAds(configs);
}
}
Cannot extend `AdsRefresh` [1] with `AdsRefreshTiler` because property `placeholderIndex` is missing in `AdType` [2] but exists in object type [3] in property `ads`.Flow(InferError)
It doesn't look like Flow supports overriding types and is complaining about the type conflict for the "ads" field in the parent and the child. You aren't allowed to change the type of a field that's been defined in the parent in the child.
This is so the child parent relationship is maintained. If you change the type of one of the fields on a child class the functions that you defined in the parent may no longer function when you call them on the child.
e.g.
export default class Parent {
felid1: number;
parentFunction() {
return this.felid1 / 3;
}
}
class Child extends Parent {
field1: string; // Now the parentFunction wont work since you can't divide strings
}
var a = new Parent();
a.felid1 = 1;
a.parentFunction(); // Does not crash
var c = new Child();
c.field1 = "a";
c.parentFunction(); // Crashes
You'll have to restructure you objects so this doesn't happen. Either by breaking down ads into multiple fields or by not using extends.

JS create property in a class which is the same type

I have a use case where I want to create a property in a class which is a new instance of the current class. For example:
class Test {
constructor() {
this.prop = new Test()
}
}
const t = new Test();
The problem is that I get Maximum call stack size exceeded. There is any way to do it?
Indeed, the constructor is called again when you do new Test(), so you get an endless call of the constructor.
You probably should do this one by one, and call a method explicitly when you want to "deepen" the object structure with a next instance of Test, like so:
class Test {
constructor() {
this.child = undefined;
}
createChild() {
return this.child = new Test();
}
}
const t = new Test();
const child = t.createChild();
const grandChild = child.createChild();
// ...etc
Now you control how deeply nested this structure should become. Maybe you just want one level deep? Then just call createChild only on the first instance.

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();

javascript - Check if parent methods are used inside child methods

I'm writing some JS that extends a parent class and I wanted to know if there's a way to tell if a child class is using a parent method without having called it yet. Ideally I'd like to run a check in the constructor of the parent to see if any of the child methods are using the parent's methods in the method definition.
I've done a bit of research and have come across things like Object.getOwnPropertyNames() but I'm not sure if I'm headed in the right direction.
For instance:
class Path {
constructor (name) {
// how can I check if addRelationship have been used? If possible.
this.relationships = {};
this.currentRelationship = '';
this.path = path;
}
addRelationship (relationship) {
// do something
this.currentRelationship = relationship.path;
return this;
}
makePath () {
let path = [this.path];
if(this.currentRelationship) {
path.push(this.currentRelationship)
}
return path.join("/");
}
}
class OnePath extends Path {
// ...
someMethodFromThatRelationship () { }
}
class TwoPath extends Path {
// ...
}
var onePath = new OnePath('one');
var twoPath = new TwoPath('two-path');
class SomeOtherPath extends Path {
one () {
return this.addRelationship(onePath);
}
two () {
return this.addRelationship(twoPath);
}
}
The idea of the above example is I could check if addRelationship is referenced in any methods and if so, register a this.relationships.one and this.relationships.two before one() and two() are actually called. I hope I'm making sense. I'd love to know if this is even possible.
Updated
The end result of the above code would be the ability to do the following:
let someOtherPath = new SomeOtherPath('some-other-path');
// now I can call
someOtherPath.relationships.one.someMethodFromThatRelationship();
// and can also call the save method from the extended class
someOtherPath.one().makePath();
// some-other-path/one
// I can also just call
someOtherPath.makePath();
// some-other-path
Is there a way to tell if a child class is using a parent method without having called it yet?
No. Figuring out what programs do without calling them is equivalent to the unsolvable halting problem.
I think what you are actually looking for is a more declarative approach for creating the relationship and its accompanying method in one go. Don't use too much magic (which a parent constructor inspecting its child class code would certainly be) but be explicit.
class Path {
constructor (path) {
this.relationships = {};
this.currentRelationship = '';
this.path = path;
}
addRelationship (name, relationship) {
this.relationships[name] = relationship;
this[name] = function() {
// do something
this.currentRelationship = name;
return this.relationships[name];
}
return this;
}
makePath () {
let path = this.path;
if (this.currentRelationship) {
path += "/" + this.relationships[this.currentRelationship].makePath();
}
return path;
}
}
class SomeOtherPath extends Path {
constructor(name) {
super(name);
this.addRelationship("one", new OnePath('one'));
this.addRelationship("two", new TwoPath('two-path'));
}
}
or even
class Path {
constructor (path, relationships = {}) {
this.relationships = relationships;
this.currentRelationship = '';
this.path = path;
for (let const r in relationships)
this.addRelationship(r, relationships[r]);
}
…
}
class SomeOtherPath extends Path {
constructor(name) {
super(name, {
one: new OnePath('one'),
two: new TwoPath('two-path')
});
}
}
Maybe you don't even need these child classes any more if they don't have other methods or are only instantiated once (as singletons).
Notice that the above approach will create new methods and new subpaths on every instantiation of the constructor, if you don't want that you can of course also put the declaration on the class statically. Just make addRelationShip a static method that initialises the default relationships objects and puts the methods on the class' .prototype. The variations of the pattern are endless.
You even might want to experiment with the proposed decorators feature for classes.

Categories

Resources