JS类对象变量赋值时机?子类属性为何在父构造器中未定义
JavaScript类中属性与方法的初始化时机差异解析
问题描述
我对JS中对象变量的赋值时机有些困惑。根据模糊的Java知识,我以为对象变量会在构造器调用前初始化,显式声明的属性也应如此,但实际并非如此。
父类Shape代码:
class Shape { constructor(data) { this._data = data; this._data.markup = this._generateMarkup(); } }
子类Circle代码:
class Circle extends Shape { _elementName = 'test'; constructor(data) { super(data); console.log({data}); } _generateMarkup() { console.log(this._elementName); return `<div class="${this._elementName}">Test</div>` } }
此时this._elementName返回undefined。我想知道,本质上属于子类的_generateMarkup()方法能在父构造器中访问,为何_elementName属性却不行?
原因解析
这是因为JavaScript类的字段初始化(比如_elementName = 'test')和方法定义的执行时机完全不同:
- 方法(包括子类重写的方法):在类被定义的时候就已经挂载到了子类的原型链上,所以当父类构造器调用
this._generateMarkup()时,this指向的是子类实例,能顺着原型链找到子类的_generateMarkup方法。 - 类字段初始化:是在
super()调用之后、子类构造器剩余代码执行之前才会完成的。也就是说,父类构造器执行时,子类的类字段还没被赋值,所以此时this._elementName是undefined。
换个角度看,你的Circle类构造逻辑实际等价于:
class Circle extends Shape { constructor(data) { // 1. 先执行父类构造器 super(data); // 2. 再执行类字段的赋值操作 this._elementName = 'test'; // 3. 最后执行子类构造器里的自定义代码 console.log({data}); } // 方法在类定义阶段就被添加到原型上 _generateMarkup() { console.log(this._elementName); return `<div class="${this._elementName}">Test</div>` } }
所以父类构造器调用_generateMarkup时,子类的_elementName还没完成赋值,自然返回undefined。
解决方案参考
如果要避免这个问题,有两种常见思路:
- 延迟生成标记:不在父类构造器中直接调用
_generateMarkup,而是改为在子类构造器super()调用之后再执行,此时子类字段已经初始化完成。 - 使用原型属性:把
_elementName定义在子类的原型上(比如Circle.prototype._elementName = 'test'),这样原型属性在父类构造器执行时就能通过this访问到。
内容的提问来源于stack exchange,提问作者NickW
相关产品推荐
相关产品推荐

