运行 一个 'constructor' 或函数,在 class 字段初始化之后,以一种理智的方式?

Run a 'constructor' or function, after class fields initialized, in a sane way?

我想使用 ES6 public class 字段:

class Superclass {
    constructor() {
        // would like to write modular code that applies to all
        // subclasses here, or similarly somewhere in Superclass
        
        this.example++;  // does NOT WORK (not intialized)
        //e.g. doStuffWith(this.fieldTemplates)
    }
}

class Subclass extends Superclass {
    example = 0
    static fieldTemplates = [
        Foo, 
        function() {this.example++}, 
        etc
    ]
}

问题:

ES6 public 字段未在构造函数之前初始化,仅在 current 构造函数之前初始化。例如,当调用super()时,任何子字段都还没有定义,就像this.example还不存在。静态字段将已经被定义。因此,例如,如果从 superclass 构造函数中调用适当的 .bind 来执行代码 function(){this.example++},它将失败。

解决方法:

一种解决方法是在所有 ES6 public classes 已正确初始化之后放置所有初始化逻辑。例如:

class Subclass extends Superclass {
    example = 0
    lateConstructor = (function(){
        this.example++; // works fine
    }).bind(this)()
}

有什么解决办法?

然而,这将涉及重写每个 class。我想要这样的东西,只需在 Superclass.constructor 中定义它,像 Object.defineProperty(this, 'lateConstructor', {some magic}) 这样神奇的东西(据称 Object.defineProperty 在内部是 es6 static 字段的方式定义,但我看不到这样的解释如何在 mozilla 文档中以编程方式实现这一点;在使用 Object.getOwnPropertyDescriptor 检查我上面的 immediately-.binded-and-evaluated cludge 之后,我倾向于相信没有办法将 属性 描述符定义为 thunk;该定义可能在从 super() 返回后执行,可能会立即评估并分配给 class,如 let exampleValue = eval(...); Object.defineProperty(..{value:exampleValue}))。或者我可以做一些可怕的事情,比如在 Superclass.constructor 中做 setTimeout(this.lateConstructor,0) 但这会破坏很多东西并且不能很好地组合。

我也许可以尝试只在各处使用对象的层次结构,但是有没有什么方法可以为父 class 中的所有子 class 实现一些全局逻辑?除了让吸气剂让一切变得懒惰之外?感谢您的任何见解。

参考文献:

Run additional action after constructor --(问题:这需要包装所有子classes)

Can I create a thunk to run after the constructor?

不,那不可能。

How to run code after class fields are initialized, in a sane way?

将代码放入定义这些字段的 class 的构造函数中。

Is there some way to implement some global logic for all subclasses in the parent class?

是:定义一个方法。 subclass 可以从其构造函数中调用它。

刚想到一个解决方法(可以分层组合)。以一种不太令人满意的方式回答我自己的问题(人们可以随意 post 更好的解决方案):

// The following illustrates a way to ensure all public class fields have been defined and initialized
// prior to running 'constructor' code. This is achieved by never calling new directly, but instead just
// running Someclass.make(...). All constructor code is instead written in an init(...) function.

class Superclass {
    init(opts) {  // 'constructor'
        this.toRun();  // custom constructor logic example
    }
    
    static make() {  // the magic that makes everything work
        var R = new this();
        R.init(...arguments);
        return R;
    }
}
    
class Subclass extends Superclass {
    subclassValue = 0  // custom public class field example

    init(toAdd, opts) {  // 'constructor'
        // custom constructor logic example
        this.subclassValue += toAdd;  // may use THIS before super.init
        
        super.init(opts);

        // may do stuff afterwards
    }

    toRun() {          // custom public class method example
        console.log('.subclassValue = ', this.subclassValue);
    }
}

演示:

> var obj = Subclass.make(1, {});
.subclassValue =  1

> console.log(obj);
Subclass {
    subclassValue: 1
    __proto__: Superclass
}