2

I have an old codebase full of subclasses of a some external class, using prototypal inheritance. Recently, this external class has been ported to an ES6 class, but also has new features I'd like to use. Prototypal inheritance doesn't work anymore, and I'm wondering if it's possible to make it work even if it's with some ugly hack. This is basically what I'm trying to do:

class ClassParent {
    constructor(a) {
        this.a = a;
    }
}

var ProtoChildFromClassParent = function(a) {
    ClassParent.call(this, a);
}
ProtoChildFromClassParent.prototype = Object.create(ClassParent.prototype);
ProtoChildFromClassParent.prototype.constructor = ProtoChildFromClassParent;

var child = new ProtoChildFromClassParent(4);
console.log(child.a);

I get the following error:

ClassParent.call(this, a);
                ^

TypeError: Class constructor ClassParent cannot be invoked without 'new'

Please don't post answers like "you should port your subclasses to ES6". I know that's probably the appropriate thing to do, take this question more as a learning exercise / curiosity about JS internals.

yewang
  • 585
  • 7
  • 14
  • Unfortunately, it’s completely impossible to have a regular function constructor extend an ES6 class. – Ry- Jul 14 '18 at 23:28

3 Answers3

4

Since you're running this all on an environment that actually supports real ES6 classes, you may be able to achieve what you're looking for. What you'll need to do is to change your subclass logic to be

var ProtoChildFromClassParent = function(a) {
    const _this = Reflect.construct(ClassParent, [a], new.target);
    return _this;
}
Object.setPrototypeOf(ProtoChildFromClassParent, ClassParent);
Object.setPrototypeOf(ProtoChildFromClassParent.prototype, ClassParent.prototype);

This is predicated on Reflect.construct being available, so it will not work on an older ES5 environment, but then neither would ES6 class syntax either. It's also important that new.target be available. As long as both are available, this is very close to replicating the behavior you'd get from using actual class syntax. That said, immediately the question would be why you're not just doing

class ProtoChildFromClassParent extends ClassParent {}

so whether this is useful or not really depends on what's stopping you from doing that to begin with.

loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
  • @yewang - FYI, this is NOT simpler than just porting your classes to use ES6 syntax, so I hope you're considering that. – jfriend00 Jul 15 '18 at 01:27
  • I knew that kind of comment had to appear in the answers or comments despite my disclaimer xD My codebase has already been ported to ES6 and I know that's the proper solution. Having said that, implementing the Reflect hack would have been a much faster way to just try out the new parent class implementation and see if its new version didn't break anything (except for the proto inheritance) in my old codebase that relied on it. If it didn't break my old codebase and implemented new useful features, then I would have known for sure that porting my codebase to ES6 was worth it. – yewang Jul 15 '18 at 21:06
0

I was also interested on how to inherit in prototypical way from ES6 class, just to understand more in JS and here what I can propose:

class Parent {
    constructor(data){
        this.#setPrivateProperty(data);
    }
    #privateProperty = "Parent private property";
    #setPrivateProperty = (data)=>{
        this.#privateProperty = data;
    }
    parentPublicMethod = ()=>{
        console.log("Parent public method responded:", this.#privateProperty);
    }
}

function Child(data, parentData){
    this.__proto__ = new Parent(parentData)
    this.childPublicProperty = data;
}

Child.prototype = Parent.prototype;
Child.prototype.constructor = Child;

let c = new Child("Child data", "Parent data");
 // Output: "Parent public method responded: Parent data"
c.parentPublicMethod();
// Output: "Child public property value is: Child data"
console.log("Child public property value is:", c.childPublicProperty); 
// Output: "1. Instance of Child: true 2. Instance of Parent: true"
console.log("1. Instance of Child:", c instanceof Child, "2. Instance of Parent:", c instanceof Parent);

I will be very grateful to those people, who will criticize this code sample and maybe we get some more details. Thanks to All in advance!!!

-2

class is just a friendlier syntax for older constructor function patterns.

i.e.:

const x = function () {};
const y = new x();

Is the same as:

class x {
  constructor () {}
}
const y = new x();

y.prototype refers to the constructor method of the x class.

Geuis
  • 41,122
  • 56
  • 157
  • 219