JavaScript中如何在super()调用时传递当前类实例this
我有一个继承自ComposerForm的PreferencesForm类,代码如下:
class ComposerForm extends BaseForm { constructor(formsObject, options) { super({ ...options, setup: {}, }); this.formsObject = { ...formsObject }; } // ...其他方法 }
class PreferencesForm extends ComposerForm { constructor(company, options = {}) { super( { upids: new UpidsForm(company).initialize(), featureSettings: new FeatureSettingsForm(company) }, options ); } }
现在需要在初始化FeatureSettingsForm时,把PreferencesForm的实例和company一起传递进去(类似new FeatureSettingsForm(company, {prefForm: this})),但因为在super()调用阶段无法访问this,直接实现有困难,想请教可行的解决方法。
这确实是类继承里常见的小问题——在super调用完成前,this还没绑定到子类实例上,没法直接传。不过有几种实用的办法可以解决,我给你列出来:
方法1:super调用后手动补全引用
最简单直接的方式就是先创建不带prefForm的FeatureSettingsForm实例,等super调用完成、this可用后,再给它补上引用:
class PreferencesForm extends ComposerForm { constructor(company, options = {}) { // 先初始化不带prefForm的子表单 super( { upids: new UpidsForm(company).initialize(), featureSettings: new FeatureSettingsForm(company) }, options ); // 现在this已经可用,给featureSettings设置prefForm this.formsObject.featureSettings.prefForm = this; // 如果FeatureSettingsForm是通过方法接收这个引用(比如有setter),也可以这么写: // this.formsObject.featureSettings.setPrefForm(this); } }
这个方法的前提是FeatureSettingsForm允许后续设置prefForm属性,或者提供了对应的设置方法。优点是不需要修改父类ComposerForm的代码,改动最小。
方法2:延迟初始化子表单
如果ComposerForm的构造逻辑不会立即依赖featureSettings这个子表单,我们可以先在super里传一个占位符(比如null),等this可用后再真正初始化FeatureSettingsForm并替换占位符:
class PreferencesForm extends ComposerForm { constructor(company, options = {}) { // 先传占位符,暂时不初始化featureSettings super( { upids: new UpidsForm(company).initialize(), featureSettings: null }, options ); // 现在可以正常传入this了 this.formsObject.featureSettings = new FeatureSettingsForm(company, { prefForm: this }); // 如果需要像UpidsForm那样调用initialize方法,加上即可: // this.formsObject.featureSettings = new FeatureSettingsForm(company, { prefForm: this }).initialize(); } }
这种方法适合父类不会在构造函数里立即遍历或使用所有子表单的场景,避免占位符导致的报错。
方法3:给父类添加子表单更新方法
如果有权限修改ComposerForm的代码,可以给它加一个专门更新子表单的方法,这样子类就能在super调用后优雅地注入带this的子表单:
// 修改ComposerForm,添加更新子表单的方法 class ComposerForm extends BaseForm { constructor(formsObject, options) { super({ ...options, setup: {}, }); this.formsObject = { ...formsObject }; } updateForm(formKey, formInstance) { this.formsObject[formKey] = formInstance; // 如果父类需要对子表单做统一初始化,也可以在这里处理 } } // 改造PreferencesForm class PreferencesForm extends ComposerForm { constructor(company, options = {}) { // 先只传已经能初始化的upids表单 super( { upids: new UpidsForm(company).initialize() }, options ); // 调用父类方法注入带this的featureSettings this.updateForm('featureSettings', new FeatureSettingsForm(company, { prefForm: this })); } }
这种方法扩展性更好,如果后续有其他子类也需要类似的延迟注入,都可以复用这个updateForm方法。
方法4:利用延迟执行的函数(需改造父类)
如果想让子类的代码看起来更连贯,可以把featureSettings的初始化逻辑包装成一个箭头函数,然后让ComposerForm在构造时执行这个函数并传入this:
// 修改ComposerForm的构造逻辑,处理函数类型的表单定义 class ComposerForm extends BaseForm { constructor(formsObject, options) { super({ ...options, setup: {}, }); // 遍历表单配置,执行函数获取实例 this.formsObject = Object.fromEntries( Object.entries(formsObject).map(([key, value]) => { return typeof value === 'function' ? [key, value(this)] : [key, value]; }) ); } } // 改造PreferencesForm,传入箭头函数 class PreferencesForm extends ComposerForm { constructor(company, options = {}) { super( { upids: new UpidsForm(company).initialize(), // 箭头函数接收父类传递的this(也就是当前PreferencesForm实例) featureSettings: (prefForm) => new FeatureSettingsForm(company, { prefForm }) }, options ); } }
这种方法的好处是子类代码不需要拆分初始化逻辑,但需要修改父类来支持函数类型的表单配置,适合有统一延迟初始化需求的场景。
内容的提问来源于stack exchange,提问作者prajeesh

