Search code examples
javascriptc++design-patternsequivalent

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

Solution

  • 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.