如何在JavaScript中从String实例化一个类

时间:2018-03-01 04:08:11

标签: javascript es6-class

我处于一种奇怪的情况,我需要使用存储在变量中的字符串来实例化一个新的类,但即使我确定类名是正确的,我得到的错误是给定的类名不是构造函数

这是一个无效的虚拟代码:

class Foo {
    constructor(){
        console.log('Foo!');
    }
};
const foo = 'Foo';
const bar = new window[foo]();
console.log(bar);

这个错误:

Uncaught TypeError: window[foo] is not a constructor

1 个答案:

答案 0 :(得分:2)

一种可能性是使用eval

class Foo {
    constructor(){
        console.log('Foo!');
    }
};
const foo = 'Foo';
const bar = eval(`new ${foo}()`);
console.log(bar);

您必须评估在特定情况下使用eval()的安全性。如果您知道要插入到运行eval()的代码中的字符串的来源,或者您可以先对其进行消毒,那么它可能是安全的。

我个人更喜欢查找表。如果您希望按字符串映射已知数量的类,则可以创建自己的查找表并使用它。这样做的好处是,如果字符串中有奇怪的东西,就不会产生意想不到的后果:

class Foo {
    constructor(){
        console.log('Foo!');
    }
};

class Goo {
    constructor(){
        console.log('Goo!');
    }
};

// construct dict object that contains our mapping between strings and classes    
const dict = new Map([['Foo', Foo], ['Goo', Goo]]);

// make a class from a string
const foo = 'Foo';
let bar = new (dict.get(foo))()

console.log(bar);

如果你真的要去这条路线,你可能想把它封装在一个函数中,然后如果在dict中找不到字符串就添加错误处理。

这应该比使用全局或Window对象作为查找机制更好,原因有两个:

  1. 如果我记得,ES6中的class定义不会像其他顶级变量声明那样自动放在全局对象上(Javascript试图避免在先前的设计错误之上添加更多垃圾)

  2. 因此,如果您要手动分配给查找对象,您也可以使用不同的对象而不会污染全局对象。这就是dict对象在这里的用途。