子 class 中的字段初始化发生在构造函数在父 class 中完成后,在 javascript 中。这是设计使然吗?

Field initialization in a sub-class happens after constructor finishes in the parent class, in javascript. Is this by design?

展示比描述更容易。这是代码

let ns = {};

ns.A = class {
    constructor() { 
        this.Virtual();
    }   

    Virtual() {
    }
};

ns.B = class extends ns.A {
    constructor() {
        super();
        alert(this.Field);
    }

    Field = 0;  

    Virtual() {
        this.Field = 123;
    }
}

alert()表示this.Field等于0,即Bclass中的字段初始化是在A构造函数完成后进行的。这是 "by design" 中的 Javascript 吗?

如果我将 Field 放在 B class 的原型中,那么一切正常,就像在任何其他语言中一样。例如

let ns = {};

ns.A = class {
    constructor() { 
        this.Virtual();
    }   

    Virtual() {
    }
};

ns.B = class extends ns.A {
    constructor() {
        super();
        alert(this.Field);
    }

    //Field = 0;    

    Virtual() {
        this.Field = 123;
    }
}

ns.B.prototype.Field;

很抱歉打扰你,但我不知道在哪里报告这个问题是正确的。

来自https://github.com/tc39/proposal-class-fields#execution-of-initializer-expressions

When field initializers are evaluated...

Base class: At the beginning of the constructor execution...

Derived class: Right after super() returns...

插图:

class A {
    constructor() {
        console.log('A constructor start');
        this.Virtual();
        console.log('A constructor end');
    }

    Field = (() => { console.log('A field init'); return 1})()

    Virtual() {
    }
};

class B extends A {
    constructor() {
        console.log('B constructor start')
        super();
        console.log('B constructor end')
    }

    Field = (() => { console.log('B field init'); return 2})()

    Virtual() {
        console.log('B virtual')
        this.Field = 123;
    }
};

console.log(new B())

也就是说,在您的代码中,Field = 0 发生在 this.Field = 123 之后,因此会覆盖它。声明的顺序无关紧要。

如果您对此行为有疑问并希望进行讨论,https://github.com/tc39/proposal-class-fields/issues 将是正确的地方。