子类通过父类构造函数赋值属性后为何为undefined?如何解决?
问题原因分析
你遇到的问题核心在于类公共字段的初始化时机:
当你在B类里声明b;时,这是一个ECMAScript类的公共字段。在实例化子类B的过程中,代码执行顺序如下:
- 进入B的构造函数,打印
1,调用super() - 执行A的构造函数,打印
2,调用this.inv()——此时因为原型链的关系,this指向B的实例,所以调用的是B类重写的inv方法 - 执行B的
inv方法:- 打印
3,调用super.inv()执行A的inv,打印4并给this.a赋值为a value - 回到B的
inv,打印5,给this.b赋值为b value,此时打印this.b确实是b value
- 打印
- 关键步骤:当
super()执行完成后,JavaScript会自动初始化B类的公共字段b,将其设置为undefined,直接覆盖了刚才在inv里给this.b赋的值 - 最后打印
b.a(不受影响,因为A类的a字段在super.inv()里赋值后,A的构造函数执行完没有后续的字段初始化覆盖它),b.b就变成了undefined
解决方案
这里提供几种可行的解决方式:
方式一:移除子类的公共字段声明
如果不需要提前声明b字段(JavaScript允许动态添加属性),直接删掉B类里的b;即可:
class A { a; constructor() { console.log(2); this.inv(); } inv() { console.log(4); this.a = "a value"; } } class B extends A { // 删掉这里的b; constructor() { console.log(1); super(); } inv() { console.log(3); super.inv(); console.log(5); this.b = "b value"; console.log(this.b); console.log(this.constructor === B); } } const b = new B(); console.log(b.a); // a value console.log(b.b); // b value
方式二:将子类字段赋值移到构造函数中
把this.b的赋值逻辑放到B的构造函数里,在super()之后执行,这样就不会被字段初始化覆盖:
class A { a; constructor() { console.log(2); this.inv(); } inv() { console.log(4); this.a = "a value"; } } class B extends A { b; constructor() { console.log(1); super(); // 在这里赋值 this.b = "b value"; } inv() { console.log(3); super.inv(); console.log(5); console.log(this.constructor === B); } } const b = new B(); console.log(b.a); // a value console.log(b.b); // b value
方式三:使用私有字段(如果需要字段封装)
如果需要明确字段的存在且希望封装,可以使用私有字段#b,此时需要在构造函数里初始化,不会被自动覆盖:
class A { a; constructor() { console.log(2); this.inv(); } inv() { console.log(4); this.a = "a value"; } } class B extends A { #b; // 私有字段 constructor() { console.log(1); super(); this.#b = "b value"; } inv() { console.log(3); super.inv(); console.log(5); console.log(this.constructor === B); } // 提供getter访问私有字段 get b() { return this.#b; } } const b = new B(); console.log(b.a); // a value console.log(b.b); // b value
内容的提问来源于stack exchange,提问作者leopik
相关产品推荐
相关产品推荐

