How do I call a super constructor outside a constr

2019-06-14 02:16发布

问题:

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(); }
}

回答1:

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.