Super context in subclass call (javascript) - javascript

I have a class map as shown below:
class A {
do(input) { return input + ' A'; }
}
class B extends A {
do(input) { return input + ' B'; }
run() { return `This call: ${this.do()}, super call: ${super.do()}`; }
}
class C extends B {
do(input) { return input + ' C'; }
}
let inst = new C();
console.log(inst.run());
I expect one of two situations:
In this call this.do will be class C do and super.do will be class B do (as we call run from C and his super is B)
In this call this.do will be class B do and super.do will be class A do (as we declare run in B and his super is A)
But i find out that this and super have different contexts: this is class C and super is class A.
My real task is running only class B do and i can't find the way to do it.

Related

Is there a way to instantiate a new instance of a subclass from inside a super class method?

I would like to be able to instantiate a new instance of a subclass from inside a superclass method.
If I have just a single class with no inheritance, it is straight forward:
class A {
static build(opts) {
return new A(opts)
}
makeAnother() {
return A.build(...)
}
}
const a = new A()
const a2 = a.makeAnother()
This works. However, it doesn't work with subclassing:
class B extends A { ... }
const b = new B()
const b2 = b.makeAnother() // this returns an instance of A, not B
I suppose I could add the build & makeAnother methods to each subclass, but I would rather not repeat things.
You can reference this.constructor inside the super class to get to the constructor of the subclass (or the super class itself, if the method is called on a super instance rather than a sub instance):
class A {
static build(theClass) {
return new theClass()
}
makeAnother() {
return A.build(this.constructor)
}
}
const a = new A()
const a2 = a.makeAnother()
console.log(a2 instanceof A);
class B extends A { }
const b = new B()
const b2 = b.makeAnother()
console.log(b2 instanceof B);
You'll want to use
class A {
static build(opts) {
return new this(opts)
}
makeAnother() {
return this.constructor.build(...)
}
}
Unless build does more than shown here, you don't need it at all of course, you'd rather directly return new this.constructor(...) in makeAnother.

How to implement this C++ pattern in Javascript?

A method from an inherited class should return the object type from who has inherited. In C++ this behaviour is easy to accomplish. But I don't know how to do it in javascript. I know it is wrong but I wrote like this.
class A {
someMethod() {
return new A();
}
}
class B extends A {
}
var b = new B();
b.someMethod() // should return an object of type B not A
in C++ this is easy to do
template <typename c>
struct a
{
a() : _value(NULL) {}
a(std::string v) : _v(v) {}
static c* from_string(std::string &v)
{
return new c(v);
}
private:
std::string _v;
};
struct b : public a<b>
{
b() : b<a>() {}
b(std::string &v) : node<b>(a) {}
};
How should this be implemented using javascript?
edit
This is not how inherits a class, is a particular pattern of inheriting and creating objects. There are several examples like Buffer.from, Buffer.alloc from Node.Js. But I would like to reproduce this from a base class.
A guy showed me that my issue could be solved using the following script:
class A {
method() { return this.constructor(); }
}
class B {}
var b = new B();
var b1 = b.method();
What I really would like to do is something like following.
class A {
static from() { return new this.constructor() }
};
class B extends A {};
a = A.from();
b = B.from();
a instanceof A // should be true
b instanceof B // should be true.
edit 2
I found something.
I found in typescript the same C++ behaviour can be archived as follows:
class a {
static from<t extends a>(c: { new(): t }) : t {
return new c();
}
};
class b extends a {
}
let b1 = a.from(b);
b1 instanceof b
b1 instanceof a
console.log(b1);
the es6 equivalent is:
class a {
static from(c) {
return new c();
}
}
;
class b extends a {
}
let b1 = a.from(b);
b1 instanceof b;
b1 instanceof a;
console.log(b1);
//# sourceMappingURL=index.js.map
As I've commented: a template class in C++ is actually a metaclass. It is used for constructing other classes out of it.
So we can apply this observation to JavaScript. I've played around and here's the closest thing I could get. First define a "template":
function TemplateA(cls) {
class A {
static from() {
return new cls();
};
foo() {
return -1;
};
};
return A;
};
Now define custom "extends":
function Extends(base, derived) {
// Update statics
// (these should not be overwritten)
var common = {name: 1, prototype: 1, length: 1};
var statics = Object.getOwnPropertyNames(base)
.filter(el => !common[el]);
statics.forEach(el => {
derived[el] = base[el];
});
// Update instance methods
var instanceMethods = Object.getOwnPropertyNames(base.prototype);
instanceMethods.forEach(el => {
derived.prototype[el] = base.prototype[el];
});
};
and finally usage:
class B {
test() { return 1; }
};
> Extends(TemplateA(B), B);
> var b = B.from();
> b instanceof B;
true
> var x = new B();
> x.foo();
-1
> x.test();
1
It seems to do what you want. This has some drawbacks though. It is not really an inheritance (the prototype is just updated). In particular B is not a subclass of A<B> (actually no such class even exists after Extends). On the other hand JavaScript's inheritance/prototyping is quite unique and very different from C++ and so we can't expect everything to work.
Side notes:
Is it safe? Probably. However it does require lots of discipline from a dev. For example it is too easy to overwrite something you didn't want to.
Would I use it in prod environment? Unlikely. Whatever you are trying to accomplish most likely can be achieved in some other, standard way. Treat the solution as an academic fun.
Finally: who told you that C++ way is the best way? Your swimming skills are not really useful when you are climbing, right? So I strongly suggest you rethink your entire architecture and try to do things differently.

Use all functions of another object in javascript

I couldn't find the solution to this issue. I am banging my head against a wall entire day.
Assume we have the following hierarchy of classes:
class A {
async name() { ... }
}
class B extends A {
async age() { ... }
}
class C extends A {
async group() { ... }
}
class D extends B {
constructor() {
super(...)
this.C = new C(...);
}
async group() { return await this.C.group(); }
}
Because JavaScript doesn't support multiple inheritance, I have an issue. I would like to combine prototypes of objects in a way that I can invoke function group() without the need for writing repetitive code like above. It is absolute must to be able to invoke group() directly on the object D outside of the class, even though it is the an instance of class C.
How can I combine their prototypes? I've tried things like
class A {
appendPrototype(obj) {
if (obj instanceof A)
for (let k in obj.__proto__) {
this.__proto__[k] = obj.__proto__[k];
}
}
}
.
.
. // Same two classes, B and C
class D extends B {
constructor(...) {
super(...);
const c = new C(...);
this.appendPrototype(c);
}
}
I can't invoke the function group() an instance of the class D. Why is that? How can I achieve it, without writing repetitive code like in the first example?
What you can do, it is to assign to the prototype of the class D, the method of the class C.
In this way all the instances of D will have the group method which is the same method defined within the class C, and you can do the same for all the other child classes.
class A {
async name() {
console.log('A - name');
}
}
class B extends A {
async age() {
console.log('B - age');
}
}
class C extends A {
async group() {
console.log('C - group');
}
}
class D extends B {
constructor() {
super();
}
}
D.prototype.group = C.prototype.group;
const d = new D();
d.group();
Anyway, in your case, I would go for composition (like you did with C within D) and then call the methods like:
d.c.group()
which allows you to avoid the appendPrototype logic.

In ES6, how can I splice the prototype chain?

If I have this code:
class Z { foo () {return 'from Z';} }
class A { foo () {return 'from A';} }
class B extends A { }
class C extends B { }
var c = new C();
I know I can get the prototype chain this way:
consonle.log(c.foo());
var obj = c;
while(Object.getPrototypeOf(obj)) {
obj = Object.getPrototypeOf(obj);
console.log(obj);
}
// Puts out:
// from A
// C {}
// B {}
// A {}
// {}
How can I insert an instance of Z into the prototype chain between the A {} and B {} such that the same script puts out the following?
// from Z
// C {}
// B {}
// Z {}
// A {}
// {}
Update
To clarify, I'd like to update the prototype chain such that NEW INSTANCES of C can take advantage of Z's .foo() method.
Update 2
Ok, I can accept that this isn't possible. How can I splice a particular instance of Z into the prototype chain of a particular instance of C?
I'd like to update the prototype chain such that NEW INSTANCES of C can take advantage of Z
That's not possible. If you want to insert Z as the prototype of B, then everything that inherits from B will also get Z properties. That includes all B and old C instances.
To distinguish them and not affect the old instances, you would need to make a brand new B and C.
If you still want to do it, here's how:
class A { foo () {return 'from A';} }
class B extends A { }
class C extends B { }
// Oops, B should inherit from Z not A:
class Z extends A { foo () {return 'from Z';} }
Object.setPrototypeOf(B, Z);
Object.setPrototypeOf(B.prototype, Z.prototype);
var c = new C();

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