What is proper way of inheritance is JS with initialization method - javascript

I have such code. And it works like I expect.
class colorObj {
constructor() {
this.color = new Color();
}
init() {
this.color.setColor(
calcColor() // Could be long
);
}
}
class sizeObj extends colorObj {
constructor() {
super();
this.size = new Size();
}
init() {
super.init();
this.size.setSize('10x10');
}
}
let obj = new sizeObj();
obj.init(); // (1)
obj.color.setColor('green');
obj.init();
But now I want to remove first init() call (1). And I can't understand how can I call it inside class hierarchy. I can't call this.init() in colorObj, because new Size(); have not been done yet.
Main idea is to split object allocation in constructor from it's initialization in init() method.
Also I want to have opportunity to "reset" object in any place with init() call. Without new object allocation.
I can make it with such code:
class sizeObj extends colorObj {
constructor() {
super();
this.size = new Size();
sizeObj.prototype.init.call(this, false);
}
init(initializeSuper = true) {
if ( initializeSuper ) {
super.init();
}
this.size.setSize('10x10');
}
}
It works. But I hope to find less awful solution.
Also I can do that:
class sizeObj extends colorObj {
constructor() {
super();
this.size = new Size();
this.initSizeObj();
}
init() {
super.init();
this.initSizeObj();
}
initSizeObj() {
this.size.setSize('10x10');
}
}
But it is also looks for me like tangled solution.
Thank you :)

Related

import a method from a class into another class JS

I'm still beginner in JS and I feel like I'm often dealing with some basic stuffs out here.. I want to retrieve/import a method from a class A into class B (using modules). Also, I don't know if my code is well built so I'm open for all suggestions and advices.
Here's class 1 (A)
export default class A {
constructor() {
this.zone = document.getElementById('#zone');
this.array = [];
this.init();
}
init() {
//code
}
methodToImport(id) {
let DOM = `<h1>HTML Content</h1>`
this.zone.insertAdjacentHTML('beforeend', DOM);
this.array.push(new B(this.zone.lastElementChild));
}
and here's class 2 (B)
import A from "./A.js";
class B {
constructor(element) {
this.init();
}
init() {
// here I want to retrieve the array duly wholy fillen with instances of class A
console.log(A.array);
}
}
I really wish you could help, thank you guys !
you can create an object of class A in class B and use it.
class A {
constructor() {
this.zone = document.getElementById('#zone');
this.array = [1,2,3,4];
}
}
class B {
constructor(element) {
this.a = new A;
}
}
b = new B
console.log(b.a.array);

Modify the Constructor Function of a Class

I already have a function for modifing other class methods:
class MyClass {
constructor() {
this.num = 1;
}
inc(){
this.num++;
}
}
runAfterMethod(classHandle, methodName, executeAfter) {
const oldHandle = classHandle.prototype[methodName];
classHandle.prototype[methodName] = function () {
const returnValue = oldHandle.apply(this, arguments);
//#ts-ignore
executeAfter.apply(this, arguments);
return returnValue;
};
}
runAfterMethod(MyClass, "inc", function() {console.log(this.num)})
new MyClass().inc(); // Prints 2
However, this does not work for the constructor of the class (because it is technically the class itself)
What I would really be happy with is something that mimics this behavior:
class MyClass {
constructor() {
this.num = 1;
}
}
extendConstructor(MyClass, function(){
this.str = "hello world"
})
new MyClass(); // num: 1, str: "hello world"
I have looked at things like How to modify the constructor of an ES6 class however, they all require = somewhere, which does not work inside of a function (where that only changes the value of the class inside the function, not the class itself)
Mutating the existing class is really weird. What would be much less of a code smell IMO would be to create a wrapper around the class that instantiates the class, then calls it with your custom callback, and then returns the instance - it'd be a lot more straightforward. Would that work for your purposes?
class MyClass {
constructor() {
this.num = 1;
}
}
const extendConstructor = (theClass, callback) => function(...args) {
const instance = new theClass(...args);
callback.call(instance);
return instance;
};
const Extended = extendConstructor(MyClass, function(){
this.str = "hello world"
})
console.log(new Extended()); // num: 1, str: "hello world"

Referencing this or instance in javascript singleton

I was dabbling with singletons and extending classes and came up with this 'working' code.
let instance = null;
class Motorcycle {
constructor(engine, color) {
if(!instance) {
this.engine = engine;
this.color = color;
instance = this;
} else {
return instance;
}
}
}
class Car extends Motorcycle {
constructor(engine, color) {
if(!instance) {
super(engine, color);
this.doors = 4;
} else {
return instance;
}
}
}
class SUV extends Car {
constructor(engine, color, doors) {
if(!instance) {
super(engine,color);
instance.doors = doors == undefined ? 5 : doors;
} else {
return instance;
}
}
}
I realized that in the constructor for sub-classes I can set doors with instance.doors and this.doors.
So I have two questions:
Does it matter enough to have a best practice?
What is that best practice?
Does it matter enough to have a best practice?
Not in your case probably, but there is a difference between them.
What is that best practice?
Do not use singletons. Especially not when subclassing. Your code is unable to create an instance of a separate subclass, is unable to create multiple cars, is unable to create a new SUV after creating a new Car (and also a Car is not a MotorCycle).
Your singleton objects should exist outside the constructor; preferably inside a map and accessed by their class name. Constructors should really only call their super constructor or set instance variables.
You will also need a way to dynamically bind constructor arguments, this is taken care of in the ctorBind function below.
You could also call getSingleton(SUV) a second time without args. This example just shows that the original SUV was not modified upon returning.
// REFLECTION UTILS
const getClassName = (obj) => obj.toString().match(/ (\w+)/)[1]
const ctorBind = (ctor, args) => new (ctor.bind.apply(ctor, [null].concat(args)))()
// MAIN FUNCTION
const main = () => {
console.log(getSingleton(SUV, 'V6', 'Red', 5))
console.log(getSingleton(SUV, 'V8', 'Blue', 6)) // Not updated...
}
// BEGIN SINGLETON MAP AND LOOKUP
const vehicleSingletons = {};
function getSingleton(vehicleClass, ...args) {
const className = getClassName(vehicleClass)
if (vehicleSingletons[className] == null) {
vehicleSingletons[className] = ctorBind(vehicleClass, args);
}
return vehicleSingletons[className];
}
// BEGIN CLASS DEFINITIONS
class Vehicle {
constructor(engine, color) {
this.engine = engine;
this.color = color;
}
}
class Motorcycle extends Vehicle {
constructor(engine, color) {
super(engine, color);
}
}
class Car extends Vehicle {
constructor(engine, color, doors) {
super(engine, color);
this.doors = doors;
}
}
class SUV extends Car {
constructor(engine, color, doors) {
super(engine, color, doors);
}
}
main()
.as-console-wrapper { top: 0; max-height: 100% !important; }

Possible to make Class/Instance-wide getters & setters in JS?

I am trying to implement a singleton class in JS with a ton of getters and setters. It would be much more convenient if I could define a prototype/parent to all of the getters & setters in this class, as they will all need to try and initialize the singleton before returning a value
export class GameData {
constructor() {
this._level = 0;
this._score = 0;
}
static _init() {
if (!GameData._inst) {
GameData._inst = new GameData();
}
}
static clear() {
GameData._inst = null;
}
/*
Properties
I do not want to call GameData.init() on every property, any way to only do this once?
*/
static get level() {
GameData._init();
return GameData._inst._level;
}
static set level(val) {
GameData._init();
GameData._inst._level = val;
}
static get score() {
GameData._init();
return GameData._inst._score;
}
static set score(val) {
GameData._init();
GameData._inst._score = val
}
Yep, stop making singletons. And getters. And setters.
class GameData {
constructor() {
this.level = 0;
this.score = 0;
}
}
export default new GameData();

How do I call a super constructor outside a constructor?

Now that JavaScript has classes I'm wondering how it is possible to invoke a super constructor outside of a class constructor.
My unsuccessful naive attempt (results in a SyntaxError):
class A
{
constructor() { this.a = 1; }
}
function initB()
{
super(); // How to invoke new A() on this here?
this.b = 2;
}
class B extends A
{
constructor() { initB.call(this); }
}
I'm aware that in some other language like Java a super constructor can only be invoked inside the constructor of a derived class, but ES6 classes are syntactic sugar for prototype-based inheritance, so I'd be surprised if this were not feasible using built-in language features. I just can't seem to figure out the proper syntax.
The best I've come with so far feels terribly like cheating:
class A
{
constructor() { this.a = 1; }
}
function initB()
{
let newThis = new A();
newThis.b = 2;
return newThis;
}
class B extends A
{
constructor() { return initB(); }
}
Every constructor of a class that extends something must contain a direct super(…) call.
Direct super(…) calls can only be placed in constructors. There's really no way around this.
You really should not place the initialisation logic of a class anywhere else than in its constructor. The straightforward and proper solution is not to use initB at all:
class A {
constructor() { this.a = 1; }
}
class B extends A {
constructor() {
super();
this.b = 2;
}
}
That said, there is a way to subvert the "super() call must be in the constructor" requirement. Putting it inside an arrow function counts as well! So you could do
class A {
constructor() { this.a = 1; }
}
function initB(_super) {
var b = _super();
b.b = 2;
}
class B extends A {
constructor() {
initB(() => super());
}
}
Promise me to not ever do that, please.
Another pattern is not to call super() at all, which works as long as you return an object from the constructor. With that, you can put the actual construction of the object anywhere else:
class A {
constructor() { this.a = 1; }
}
function makeB() {
var b = Reflect.construct(A, [], B); // call the A constructor with B for the prototype
b.b = 2;
return b;
}
class B extends A {
constructor() {
return makeB();
}
}
Which really isn't much better.

Categories

Resources