Use overwritten value in parent constructor - javascript

I'm invoking a function in constructor where I want to use the variable from child in the function call that's made through parent constructor. For demo purpose I've created a small script to show what I want to do:
class A{
intVal = 1;
constructor() {
this.identifyClass();
}
identifyClass()
{
console.log("I am class A", this.intVal); // This should be the value that B has overwitten
}
}
class B extends A
{
intVal = 2;
}
const x = new B();
So I'd want that the function in parent constructor should use the value that was overwritten by B ie. intVal = 2 but currently it uses the original value. Can I use any workaround for this so that I don't have to create a constructor in B and then invoke the function there?
Note: It's a very complex app where I don't want breaking changes to A which is being used at a lot of places and the Class B is being exposed to public so I don't want the people using Class B to change anything if possible where currently they just overwrite the instance variable

Ok, I think I got it. We create a symbol, if the last argument passed to the constructor is not the symbol, then we create a new object, using this.constructor, passing in the original arguments, plus the symbol, and then call identify. This effectively separates the call to identify from the constructor, allowing us to get this.intVal from the subclass, while still being transparent to the user of the subclass.
const _sym = Symbol();
class A {
intVal = 1;
constructor() {
const args = Array.from(arguments);
const _ = args[args.length - 1];
if (_ !== _sym) {
args.push(_sym);
const el = new this.constructor(...args);
el.identify();
return el;
}
}
identify() {
console.log(this.intVal);
}
}
class B extends A {
intVal = 2;
}
const x = new A();
const y = new B();

Related

How to use instanceof with a class which is not defined in the current context?

This is going to be a bit tricky but I'll do my best to explain,
Consider the following code:
class A { a() { return true; } }
class B { b() { return new A(); } }
var b = new B();
console.log(b instanceof B); // true
console.log(b.b() instanceof A); // true <--- [1]
It's pretty straightforward to see that (class B).b() is going to return an instance of an object of type(/class) A. And we can evaluate this using the instanceof operator [1].
Now, a problem arises when, for whatever reason, we do not have a definition for class A in our current scope. One scenario where such thing may happen, is when you import/require an object from a library and many of its internal classes are not exposed.
Since there is no definition for A, it is not possible to do <symbol> instanceof A ...
So, how may one actually perform this check under such scenario?
PS: I already tried the Object.prototype.toString... trick to not avail.
You could create a function that traverses the prototype chain and returns a list of all the super classes of an object including the class from which the object was instantiated from.
class Foo {}
class X extends Foo{}
class A extends X{}
class B { b() { return new A(); } }
function getParents(obj) {
const arr = [];
while (obj = Reflect.getPrototypeOf(obj)) {
arr.push(obj.constructor.name);
}
return arr;
}
var b = new B().b();
const parents = getParents(b);
console.log(`b instance of A = ${parents.includes('A')}`);
console.log(`b instance of X = ${parents.includes('X')}`);
console.log(`b instance of Foo = ${parents.includes('Foo')}`);
console.log(`b instance of Object = ${parents.includes('Object')}`);
You could also do this using a recursive function
class Foo {}
class X extends Foo{}
class A extends X{}
class B { b() { return new A(); } }
function getParents(obj, arr = null) {
if (!arr) arr = [];
const protoTypeObj = Reflect.getPrototypeOf(obj);
if (!protoTypeObj) return;
arr.push(protoTypeObj.constructor.name);
getParents(protoTypeObj, arr);
return arr;
}
var b = new B().b();
const parents = getParents(b);
console.log(`b instance of A = ${parents.includes('A')}`);
console.log(`b instance of X = ${parents.includes('X')}`);
console.log(`b instance of Foo = ${parents.includes('Foo')}`);
console.log(`b instance of Object = ${parents.includes('Object')}`);

Instantiating a class object with window[string]() [duplicate]

This question already has answers here:
Javascript ES6 class definition not accessible in window global
(2 answers)
Closed 3 years ago.
class Unit {
constructor(){
}
}
var str = "Unit";
var a = new window[str](); // error
var b = new window["Unit"](); // error
var u = new Unit(); // works
(u instanceof Unit) // true
I only recently made the switch to ES 6 syntax in regards to declaring classes. Im pretty sure that formerly i could instantiate a class like this, however ever since i have used "class" syntax, the instantiation of an object by windowclassName is not working anymore.
What exactly am i missing here ?
Variables declared with class behave similarly to those declared with const and let - they do not get implicitly assigned to the window object (which is arguably a good thing). If you wanted to put Unit on window, you would have to do so explicitly:
class Unit {
constructor(){
console.log('constructing');
}
}
window.Unit = Unit;
var str = "Unit";
var a = new window[str]();
You might consider using your own object of classes to avoid global pollution:
const myClasses = {
Unit: class Unit {
constructor(){
console.log('constructing');
}
}
};
var str = "Unit";
var a = new myClasses[str]();
Object values cannot reference each other while in the process of declaring an object literal - to put a subclass on myClasses that extends one of the existing classes, you'll have to do so outside of the myClasses declaration:
const myClasses = {
Unit: class Unit {
constructor(){
console.log('constructing');
}
}
};
myClasses.Child = class Child extends myClasses.Unit {
constructor() {
super();
console.log('Child running');
}
}
var str = "Child";
var a = new myClasses[str]();

Reference to class property is null

I was under the impression that anything that is part of an object is set by reference. This doesn't seem to be the case in this example
class System {}
class Foo {
constructor() {
this.system = null;
this.dictionary = {
system: this.system
}
}
}
class Bar extends Foo {
constructor() {
super();
this.system = new System();
}
}
var bar = new Bar();
console.log(bar.system); // System{}
console.log(bar.dictionary.system); // null
I would expect that dictionary is holding a reference to this.system which starts as null, but only because what it is referencing is null. However as can be seen, bar.dictionary.system is actually still null even though its reference was updated.
Can anyone explain what is happening here?
It's still being set by reference. The issue here is that by writing this.system = new System() you're not modifying the referenced value, you're making this.system reference a different value altogether. Meanwhile, this.dictionary.system is still pointing towards the old value.
Consider the following code:
class Foo {
constructor() {
this.system = {};
this.dictionary = { system: this.system };
}
}
class Bar {
constructor() {
super();
this.system.bananas = 5;
}
}
this would correctly add bananas to this.system and this.dictionary.system, as you're modifying the value being referenced, not making this.system reference a different value entirely.
You are trying to use a value of system, which is assigned, after you actually want to use it. That's like the follwing, which won't also work
var x = null;
console.log(x);
x = "Hallo Welt";
The problem in your case is, you couldn't simply swap the call of super() with the assignment - which would solve the problem. Instead you could use properties or functions, which are getting overridden in the child class and return the actual value.
class Foo {
constructor() {
this.dictionary = {
system: this.system
}
}
get system() { return null; }
}
class Bar extends Foo {
constructor() {
super();
}
get system() {
return this._system ?
this._system :
this._system = new System();
}
}
You are assigning this.system after you call the base constructor. If you do this, you don't update dictionary once you call this.system = new System();
Here is a rough imitation of what the compiler sees:
this.system = null;
this.dictionary = {
system: this.system
}
this.system = new System();
See how you are not actually updating this.dictionary.
Imagine I have this code:
var a = 5;
var b = a;
a = 6;
At this point, a is 6 and b is 5. This is because I set b to the previous value of a. If I want to update b, I have to equate them again.

How to implement instance counter in es6?

I was learning es6 so I was trying to convert these code from es5 to es6.
I knew how to make an instance counter in es5. The id built on A.prototype and counter built on A itself. When I built a instance by A, it will trigger counter++ to set id. Thus, it implements inheritance counter of action.
var A = (function () {
function A() {
this.id = ++A.counter;
console.log(this.id);
}
A.counter = 0;
return A;
}());
a = new A(); // 1
b = new A(); // 2
c = new A(); // 3
If I worked in es6, how do I implement same feature?
The other two answers are completely correct. But if you wanted to get super crazy ES6 you could make getters for the properties.
class A {
constructor() {
this._id = A.counter;
console.log(this.id);
}
get id() {
return this._id;
}
static get counter() {
A._counter = (A._counter || 0) + 1;
return A._counter;
}
}
a = new A() // <- 1
b = new A() // <- 2
c = new A() // <- 3
That way your counter and id are both read only, and your counter increments automatically every time you access it...plus it's all tidy inside the class definition instead of needing to go outside it.
Not saying you need to do it this way...but it seems like you're learning about ES6 and this example shows a couple of neat tricks about what you can do with it.
You'd do it in exactly the same way as in ES5:
class A {
constructor() {
this.id = ++A.counter;
console.log(this.id);
}
}
A.counter = 0;
var a = new A(); // 1
var b = new A(); // 2
var c = new A(); // 3
(you can also add the same unnecessary IIFE if you want)
The es6 class can complete you wanted.
id isn't built on A.prototype, but on each A instance. The constructor is to do a instance. So you can look, it does this.id = ++A.counter when class A is built a new instance.
class A {
constructor(){
this.id = ++A.counter;
console.log(this.id)
}
}
A.counter = 0;
a = new A()
b = new A()
c = new A()
Here is a solution which is also fully contained in an ES6 class as BryanGrezeszak's answer but with a different behavior that may be useful in some cases:
class A {
constructor() {
this.constructor.counter = (this.constructor.counter || 0) + 1;
this._id = this.constructor.counter;
console.log(this.id);
}
get id() {
return this._id;
}
}
class B extends A {}
class C extends A {}
a = new B(); // <- 1
b = new C(); // <- 1
c = new B(); // <- 2
d = new A(); // <- 1
The difference with this code is that inherited classes have their own independent counter.
If you don't want to add properties to the constructor, you can replace
this.constructor.counter
with
Object.getPrototypeOf(this).counter
that is the current way of expressing the deprecated
this.__proto__.counter
That way counter will be a static property of the class rather than a property of the (static function) constructor.

Is there a way to keep super relative to the subclass?

If I define two classes as follows...
class A {
getParentInstance(...args) {
return new super.constructor(...args);
}
}
class B extends A {}
console.log((new B).getParentInstance().constructor.name);
Object is logged to the console, instead of my desired A. This is due to the super in A.prototype.getParentInstance referencing the superclass of A specifically which is Object. This is opposed to the alternative, which would be super being relative to the current level in the prototype chain -- for B that would be A.
My question is: Is there a way to define methods in such a way to use the relative super at each level of the prototype chain when inherited? Effectively resulting in...
(new B).getParentInstance().constructor.name === 'A'
You could try something like this
class A {
static getParentConstructor() {
return Object;
}
}
class B extends A {
static getParentConstructor() {
return A;
}
}
var b = new B();
var a = new (b.constructor.getParentConstructor())();
A little Object.getPrototypeOf() magic seems to be the trick:
class A {
getParentInstance(...args) {
const thisProto = this.constructor.prototype;
const RelativeSuperConstructor = Object.getPrototypeOf(thisProto).constructor;
return new RelativeSuperConstructor(...args);
}
}
class B extends A {}
console.log((new B).getParentInstance().constructor.name);
results in the correct "super" being grabbed, and thus logs 'A' as desired.

Categories