类字段中传递类方法实例化新类报错:this.a未定义且构造函数未执行
这个问题的核心在于类字段的初始化顺序,结合Babel的转译逻辑,咱们一步步拆解:
问题根源
你写的b = new Def(this.a)属于类实例字段,在ES规范以及Babel的转译逻辑中,实例字段的初始化会优先于构造函数体内部的代码执行。更直白地说,Babel会把类字段的赋值逻辑移到构造函数的最顶部,完全不管你原来构造函数里的代码顺序。
你的代码转译后大概是这个样子:
function Abc() { // 先执行类字段的赋值 this.b = new Def(this.a); // 再执行构造函数体里的代码 this.a = () => {}; }
问题就出在这里:当执行this.b = new Def(this.a)时,this.a还没有被赋值(构造函数里的this.a = () => {}还没执行),所以传入Def的是undefined。如果Def的构造函数里尝试访问这个undefined的某个属性(比如callback.a),就会抛出cannot read property a of undefined的错误。
而且因为这个错误发生在构造函数体代码执行之前,所以构造函数里的this.a = () => {}根本没机会运行,这就是你调试时看到“构造函数代码从未执行”的原因。
解决方案
解决这个问题有几种常用方式:
把
b的初始化移到构造函数内部,且放在this.a赋值之后
这样能确保初始化b时,this.a已经被正确赋值:class Abc { constructor() { this.a = () => { }; this.b = new Def(this.a); } }把
a也改成类字段,并且放在b的前面
类字段是按书写顺序初始化的,把a的定义放在b前面,就能保证初始化b时this.a已经存在:class Abc { a = () => { }; b = new Def(this.a); }延迟
Def对回调的使用
如果Def不需要在构造函数里立即使用this.a,可以改成在某个方法中调用回调,这样即使构造时传入的是undefined,后续this.a赋值后也能正常使用(不过这种方式需要修改Def的逻辑,优先推荐前两种)。
内容的提问来源于stack exchange,提问作者darKnight

