手动实施 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 未知。我不需要支持任何花哨的东西(私有的、静态的等)...我只需要 thissuper 就可以工作。提示?

不幸的是,你不能不使用 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();