手动实施 extends/super
Implementing extends/super manually
我正在根据从数据库中提取的描述性数据动态构建 类。例如,如果数据是这样的:
ClassName = ExampleParent
Method1.name = "greet"
Method1.code = "console.log(\"Parent hello\");"
像这样构建它工作得很好:
classList[ClassName] = function(){}
classList[ClassName].prototype[Method1.name] = Function(Method1.code);
var Bob = new classList["ExampleParent"]();
Bob.greet();
但我对如何动态创建继承感到困惑:
ClassName = ExampleChild
ClassExtends = ExampleParent
Method1.name = "greet"
Method1.code = "super.greet(); console.log(\"Child hello\");"
我不明白如何使用 ExampleChild.prototype 来指向 ExampleParent 并包含 ExampleChild 的自定义方法,甚至当我尝试时,它说 super 未知。我不需要支持任何花哨的东西(私有的、静态的等)...我只需要 this 和 super 就可以工作。提示?
不幸的是,你不能不使用 eval
(或者通过使用 Function
return 函数来有效地使用 eval
),但这仍然是与您正在做的事情一致,因为 Function
允许任意代码执行,就像 eval
一样。还是丑。
绝对信任数据源很重要。因为eval
(和Function
)允许执行任意代码,所以您不能(例如)允许用户 A 编写将存储在数据库中供用户 B 检索和使用的代码,而无需 绝对信任 用户 A。这会使用户 B 处于危险之中,可能会暴露他们来自用户 A 的恶意代码。
有了这个警告,您可以使用 eval
来完成,例如:
const className = "ExampleChild";
const parentName = "ExampleParent";
const methodName = "greet";
const methodCode = "super.greet();";
const C = eval(`
class ${className} extends ${parentName} {
${methodName}() {
${methodCode}
}
}`
);
您可能需要一个对象来按运行时名称存储 类,因为这是 extends
子句所需要的。
示例:
const classes = {};
function buildClass(spec) {
const extendsClause = spec.parentName ? ` extends classes.${spec.parentName}` : "";
const methods = spec.methods.map(
mspec => `${mspec.name}() { ${mspec.code} }`
).join("\n");
const cls = classes[spec.className] = eval(`
class ${spec.className} ${extendsClause} {
${methods}
}`
);
return cls;
}
const specs = [
{
className: "ExampleParent",
methods: [
{name: "greet", code: "console.log(\"Parent hello\");"}
]
},
{
className: "ExampleChild",
parentName: "ExampleParent",
methods: [
{name: "greet", code: "super.greet(); console.log(\"Child hello\");"}
]
}
];
specs.forEach(buildClass);
let c = new classes.ExampleChild();
c.greet();
您可以在子类原型中创建一个名为super
的属性,并用父类的实例设置它。
看看这个例子:
// -- Base Structure - This code is fixed, hard coded --
var classList = {};
// Register a new class
function registerClass(definition) {
var cls, i, method;
cls = function() {};
if (definition.superClass) {
cls.prototype.super = new classList[definition.superClass]();
}
classList[definition.name] = cls;
cls.constructor = cls;
for (i = 0; i < definition.methods.length; i++) {
method = definition.methods[i];
cls.prototype[method.name] = Function(method.code);
}
}
// -- Here you need to get data from your database --
// Super Class
registerClass({
name: "SuperClass",
methods: [{
name: "greet",
code: "console.log(\"Super hello\");"
}]
});
// Sub Class
registerClass({
name: "SubClass",
superClass: "SuperClass",
methods: [{
name: "greet",
code: "this.super.greet(); console.log(\"Sub hello\");"
}]
});
// Usage
var obj = new classList.SubClass();
obj.greet();
我正在根据从数据库中提取的描述性数据动态构建 类。例如,如果数据是这样的:
ClassName = ExampleParent
Method1.name = "greet"
Method1.code = "console.log(\"Parent hello\");"
像这样构建它工作得很好:
classList[ClassName] = function(){}
classList[ClassName].prototype[Method1.name] = Function(Method1.code);
var Bob = new classList["ExampleParent"]();
Bob.greet();
但我对如何动态创建继承感到困惑:
ClassName = ExampleChild
ClassExtends = ExampleParent
Method1.name = "greet"
Method1.code = "super.greet(); console.log(\"Child hello\");"
我不明白如何使用 ExampleChild.prototype 来指向 ExampleParent 并包含 ExampleChild 的自定义方法,甚至当我尝试时,它说 super 未知。我不需要支持任何花哨的东西(私有的、静态的等)...我只需要 this 和 super 就可以工作。提示?
不幸的是,你不能不使用 eval
(或者通过使用 Function
return 函数来有效地使用 eval
),但这仍然是与您正在做的事情一致,因为 Function
允许任意代码执行,就像 eval
一样。还是丑。
绝对信任数据源很重要。因为eval
(和Function
)允许执行任意代码,所以您不能(例如)允许用户 A 编写将存储在数据库中供用户 B 检索和使用的代码,而无需 绝对信任 用户 A。这会使用户 B 处于危险之中,可能会暴露他们来自用户 A 的恶意代码。
有了这个警告,您可以使用 eval
来完成,例如:
const className = "ExampleChild";
const parentName = "ExampleParent";
const methodName = "greet";
const methodCode = "super.greet();";
const C = eval(`
class ${className} extends ${parentName} {
${methodName}() {
${methodCode}
}
}`
);
您可能需要一个对象来按运行时名称存储 类,因为这是 extends
子句所需要的。
示例:
const classes = {};
function buildClass(spec) {
const extendsClause = spec.parentName ? ` extends classes.${spec.parentName}` : "";
const methods = spec.methods.map(
mspec => `${mspec.name}() { ${mspec.code} }`
).join("\n");
const cls = classes[spec.className] = eval(`
class ${spec.className} ${extendsClause} {
${methods}
}`
);
return cls;
}
const specs = [
{
className: "ExampleParent",
methods: [
{name: "greet", code: "console.log(\"Parent hello\");"}
]
},
{
className: "ExampleChild",
parentName: "ExampleParent",
methods: [
{name: "greet", code: "super.greet(); console.log(\"Child hello\");"}
]
}
];
specs.forEach(buildClass);
let c = new classes.ExampleChild();
c.greet();
您可以在子类原型中创建一个名为super
的属性,并用父类的实例设置它。
看看这个例子:
// -- Base Structure - This code is fixed, hard coded --
var classList = {};
// Register a new class
function registerClass(definition) {
var cls, i, method;
cls = function() {};
if (definition.superClass) {
cls.prototype.super = new classList[definition.superClass]();
}
classList[definition.name] = cls;
cls.constructor = cls;
for (i = 0; i < definition.methods.length; i++) {
method = definition.methods[i];
cls.prototype[method.name] = Function(method.code);
}
}
// -- Here you need to get data from your database --
// Super Class
registerClass({
name: "SuperClass",
methods: [{
name: "greet",
code: "console.log(\"Super hello\");"
}]
});
// Sub Class
registerClass({
name: "SubClass",
superClass: "SuperClass",
methods: [{
name: "greet",
code: "this.super.greet(); console.log(\"Sub hello\");"
}]
});
// Usage
var obj = new classList.SubClass();
obj.greet();