问题描述
我想要一个JavaScript类,该类可以有条件地从单独的文件中向其自身添加其他方法。这个想法是将应用程序的不同关注点分为更易于管理的独立模块,这些模块仍然可以与母应用程序类中的方法进行交互。因此,单独文件中的其他方法必须能够引用主类中的方法和变量。请参见下面的代码示例。
我看过很多不同的解决方案,但是它们都不利于我想要的东西。
- 我可以做
new Uploads()
,但是找不到Uploads.js
中的方法引用主App
类中的方法的方式 - 我可以使用
extends
,但这不允许使用条件扩展名AFAIK - I could just define new methods into the prototype itself,但这意味着外部文件将需要“知道”将要使用的类,这并不能使其广泛地重复使用。
到目前为止,我拥有的最好的是:
app.js
const Uploads = require("./Uploads.js");
const config = { hasuploads: true }; // Probably loaded from a file
class App {
constructor() {
/* Only include the separate module if the config says so */
if(config.hasuploads) {
Object.assign(this,Uploads);
}
}
foo() {
/* Something */
}
}
Uploads.js
module.exports = {
bar() {
this.foo();
}
};
它有效,但是我不知道这是否是最佳解决方案;
- 没有构造函数,因此如果
Uploads.js
需要进行一些设置,则app.js
需要包含这样做的逻辑(或至少知道调用某些唯一命名的仿构造函数方法),似乎并不理想。 - 如果
Uploads.js
包含与app.js
同名的方法或正在加载的任何其他可能的模块,则它们将被覆盖,从而导致意外的行为。 -
Uploads.js
是函数的对象,而app.js
定义了一个类。为代码的可管理性,理想(尽管我想不一定),它们都应使用相同的语法。
是否有更好/更清洁/更精细的方法?
解决方法
与其尝试执行某种疯狂的多重继承,不如尝试拥抱合成?对于解决这类问题非常有用。
class App {
constructor(modules) {
if (modules.uploads) {
this.uploads = modules.uploads(this);
}
}
foo() {
console.log('foo!');
}
}
class Uploads {
constructor(context) {
this.context = context;
}
method() {
this.context.foo();
}
}
const app = new App({ uploads: (ctx) => new Uploads(ctx) });
app.uploads.method();
您可以对此非常了解,并使用构建器来配置具有特定类型模块的应用。
根据您预期的复杂性,您可能需要考虑使用event buses
,mediators
或commands
来使事物与主机本身脱钩。
一种解决方法,可以从上传文件中覆盖覆盖现有方法的一种方法是在循环中分配新方法并检查重复方法(在这种情况下,babel.config.js
并不理想),并且仅添加一次更新:
Object.assign
更好(更干净)的替代方法可能是在使用静态类方法调用类的构造函数之前向该类添加更新,因为其const Uploads = {
bar() {
this.foo("called from bar");
}
};
const config = { hasUploads: true,// Probably loaded from a file
configured: false
};
class App {
constructor() {
/* Only include the separate module if the config says so */
if(config.hasUploads && !config.configured) {
const proto = this.constructor.prototype;
const methods = Object.keys(Uploads);
methods.forEach( name=> {
if( proto[ name] ) {
throw new Error( "App already has method " + name);
}
proto[name] = Uploads[name];
});
config.configured = true;
}
}
foo(arg) {
/* Something */
console.log( arg );
}
}
const app = new App();
app.bar();
值是构造函数。测试示例:
this
根据需要被调用
static addMethods(uploads) { // inside class declaration
const proto = this.prototype;
for (const [name,method] of Object.entries(uploads)) {
if( proto[name]) {
throw new Error("App already has a ${name} method");
}
proto[name] = method;
}
}