简体   繁体   中英

ES6, access props set by the child from the parent's constructor

I am trying to set up a class hierarchy with ES6 classes.

All entities will inherit from single Base class. It will need to access properties exposed by the Child class in a generic manner. Like this:

class Base {
    constructor() {
        this.doSomethingWithProps();
    }

    doSomethingWithProps() {
        console.log(Object.keys(this));
    }
}

class Entity extends Base {
    constructor() {
        super();

        this.key = "value";
    }
}

Obviously, in the example above, Base class will not see the key prop set up by Entity . Ideally I would move the this assignment before super() , but that's not allowed. It would also be nice to be able to set properties before constructor, but AFAIK, that's not possible either.

The only solution I am left with is doing something like the following in each Entity :

class Base {
    doSomethingWithProps() {
        console.log(Object.keys(this));
    }
}

class Entity extends Base {
    constructor() {
        super();

        this.key = "value";

        this.doSomethingWithProps();
    }
}

However, besides being less than ideal, it will also create problems if I then want to inherit from Entity . doSomethingWithProps would then need to be able to detect if it's the "top-most" method in call hierarchy and only do its thing then. The only way to achieve that (that I can think of) would involve even more boilerplate.

Is there some solution I'm missing here? I'd be open to using a different OOP pattern if needed, although I'd like to stay as close as possible to native ES6 classes.

What you are trying to do is quite impossible. The parent initialisation always runs before the child initialisation, so it is imperative that the parent constructor does not rely on properties that might be overwritten by the child. This is a language-agnostic problem, btw.

The solution is to use parameters for the constructor, which can be modified in the child before they reach the parent code:

class Base {
    constructor(key) {
        this.key = key;
        // do something with key
    }
}

class Entity extends Base {
    constructor() {
        super("value");
    }
}

console.log(new Entity);

or more generic

class Base {
    constructor(props) {
        this.doSomething(props);
        Object.assign(this, props);
    }

    doSomething(props) {
        return Object.keys(props);
    }
}

class Entity extends Base {
    constructor() {
        super({key: "value"});
    }
}

console.log(new Entity);

Also notice that constructors should always be pure. Their only purpose is to initialise the new instance from the arguments, and do nothing else like execute some side effects. A constructor should usually not need to call any (overwritable) instance methods (static methods are ok though).
So if you need to do something like that nonetheless when creating your instances, just don't do it in the constructor. Instead, call a method after using the constructor:

class Base {
    log() {
        console.log(Object.keys(this));
        return this;
    }
}

class Entity extends Base {
    constructor() {
        super();
        this.key = "value";
    }
}

var e = new Entity().log();

You can also abstract that out:

class Base {
    static createAndLog(...args) {
        var x = new this(...args);
        x.log();
        return x;
    }
    …
}
…

var e = Entity.createAndLog();

Depending on how complex you want to go, there's probably two routes you could go down.

1. Keep state in Base

If possible, pass the props you need from Entity into Base and maintain state there.

 class Base { constructor(props) { for (var k in props) { this[k] = props[k] } this.doSomethingWithProps(); } doSomethingWithProps() { console.log(Object.keys(this)); } } class Entity extends Base { constructor(props) { super(props); } } let entity = new Entity({key: 'value'}) // ["key"] 

2. Pass reference to Entity into Base

Note, I was incorrectly assuming separate instances of Entity.. this is overly redundant.

Not as simple, and will technically contain a self referencing key as Entity will inherit the child key from Base . This would definitely be considered a "wrong" approach, but I'm including it for the heck of it.

 class Base { constructor() { this.child } registerChild(child) { this.child = child this.doSomethingWithProps() } doSomethingWithProps() { console.log(Object.keys(this.child)); } } class Entity extends Base { constructor() { super(); this.key = 'value' this.registerChild(this) } } let entity = new Entity() // ["key", "child"] 

我认为这里的观察应该是:避免在构造函数中调用依赖于对象状态的方法,因为构造函数旨在构建该状态。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM