我有一个不寻常的情况,我需要动态生成一个类原型,然后使用反射工具(无法更改)遍历原型并找到所有方法。如果我可以对方法进行硬编码,类定义将如下所示:
class Calculator {
constructor(name) { this.name = name; }
}
class AdditionCalculator extends Calculator {
constructor(name) { super(name); }
add(left, right) { return left + right; }
}
class SubtractCalculator extends AdditionCalculator {
constructor(name) { super(name); }
subtract(left, right) { return left - right; }
}
但是,我无法对方法/继承进行硬编码,因为我需要大量数据驱动的方法组合:例如,我需要一个没有数学方法的计算器、一个仅包含 Add() 的计算器、一个包含仅 Subtract()、带有 Add() 和 Subtract() 的计算器,以及更多组合。
有没有办法用 JavaScript 来做到这一点(我假设是用原型)?
作为参考,这里是查找方法的反射工具:
function getMethods(obj:object, deep:number = Infinity):Array<string> {
let props:string[] = new Array<string>()
type keyType = keyof typeof obj
while (
(obj = Object.getPrototypeOf(obj)) && // walk-up the prototype chain
Object.getPrototypeOf(obj) && // not the the Object prototype methods (hasOwnProperty, etc...)
deep !== 0
) {
const propsAtCurrentDepth:string[] = Object.getOwnPropertyNames(obj)
.concat(Object.getOwnPropertySymbols(obj).map(s => s.toString()))
.sort()
.filter(
(p:string, i:number, arr:string[]) =>
typeof obj[<keyType>p] === 'function' && // only the methods
p !== 'constructor' && // not the constructor
(i == 0 || p !== arr[i - 1]) && // not overriding in this prototype
props.indexOf(p) === -1 // not overridden in a child
)
props = props.concat(propsAtCurrentDepth)
deep--
}
return props
}
例如: getMethods(new SubtractCalculator("name"));将返回 ["add", "subtract"]
这是一个解决方案(很抱歉切换到 TypeScript):
let operatorNames:Array<string> = ["add", "negate"];
let root:object = new Calculator("someName");
for (let operatorFunctionName of operatorNames) {
switch (operatorFunctionName) {
case "add":
let addPrototype = class AddClass {
add(left:number, right:number) {
return left + right;
}
};
Object.setPrototypeOf(addPrototype.prototype, root);
root = new addPrototype();
break;
case "negate":
let negatePrototype = class NegateClass {
negate(right:number) {
return -right;
}
};
Object.setPrototypeOf(negatePrototype.prototype, root);
root = new negatePrototype();
break;
}
}
return root as Calculator;
我们新建了一个基类 Calculator 的实例,它除了构造函数之外没有其他方法。然后我们将局部作用域中的派生类声明为局部变量(我尝试使用全局作用域类来执行此操作,但随后这些类被永久更改。)然后,我将局部类原型设置为将基类的实例作为原型.
然后我实例化派生类并将其存储为实例。然后重复使用更新后的实例。通过这种方式,我可以在派生类之上动态地不断添加派生类。
现在,这里有一些毛茸茸的地方,因为调用计算器构造函数的唯一方法是在创建派生类实例之前手动调用。我想我只有派生类的实例,而不是派生类本身(派生类本身无法实例化,因为不会调用计算器的构造函数。)但这没关系,因为我需要的一切是派生类的一个实例。
调用 getMethodNames(root) 会产生: ["add", "negate"] 正如预期的那样。