JavaScript、Eval和New——穷人的工厂

3
除了极少数情况,JavaScript中认为eval()是不好的实践
我刚刚遇到了一段代码片段,它通过类型名称来构造一个对象。删除错误检查、业务逻辑和上下文后,它看起来像这样:
function factory(klass) {
  eval("var obj = new " + klass + "()"); // Is there a better way?
  return obj;
}

有没有更好的方法(更安全,更干净,更快),可以在不使用eval的情况下实现类的创建?

我正在寻找一种通用的方式,通过类名来创建一个类。

例如,想象一下,代码的另一部分动态加载JavaScript文件,生成代码或允许用户扩展,但是代码的另一部分需要在工厂没有先前知识的情况下创建该类。


出于在StackOverflow上提供简洁示例的考虑,错误检查等内容被删除了——不必担心。 - Walt Stoneburner
不需要深入探讨其原因,klass是一个字符串。我们的考虑是找到一种逻辑等效的方法,使用相同的输入,而不使用eval()来完成这个特技。 - Walt Stoneburner
3个回答

5
您可以使用工厂对象注册类,然后通过字符串名称创建它们:
var allClasses = [];
allClasses["MyClass"] = function(){return new MyClass();}
allClasses["MyStruct"] = function() {return {F1:"", F2:42};}

function factory(klass) 
{
    if (!allClasses[klass]) throw "Bad name!";
    return allClasses[klass]();
}

1
我真的很想在这个问题上给出分割信用。David Titarenco在他的评论中指出,如果没有使用eval,我所尝试的事情是不可能完成的。Alexei Levenkov设法提供了下一个最接近的解决方案,按照我需要的方式获取字符串,并从工厂对象返回类。因此,我接受这个方法作为目前为止避免使用eval()的最佳方法。感谢你们所有人。 - Walt Stoneburner

3

以下是一个需要进行错误检查的情况,您不应该将其删除。假设我们采用@Gabi提出的“硬件工厂”解决方案:

function hardfactory(klass) {
  var obj = new klass();
  return obj;
}

try {
var x = hardfactory(asdf); // this will break unless
                           // we wrap every single hardfactory() call in a try-catch
} catch (err) {
    console.log(err);
} // who wants to do this every time we invoke hardfactory? not I

现在让我们来看看您原来的“软件工厂”解决方案:
function softfactory(klass) {
  try {
      eval("var obj = new " + klass + "()");
  } catch (err) {
      console.log(err);
  }
  return obj;
}

var y = softfactory("asdf"); // this will not break
                             // since we do the try catch inside the softfactory()
< p >在这种情况下,eval()调用是有充分理由的。重要的是要理解每个语言结构(是的,甚至包括eval())都有其用处,并且出现在语言中是有原因的。某些人滥用它或在错误的情况下使用它并不意味着在正确的情况下它不是一个非常强大的工具。


此外,您应该明确表示eval需要小心处理输入:如果klass类似于function(){}; /* your evil code */ (function(){}),那么您将会遇到一些麻烦。 - Gabi Purcaru
除了错误检查之外,我同意但不属于与eval相关的问题,这个答案是否确认这是使用eval的少数可接受情况之一,并且这是实现此功能的正确方式? - Walt Stoneburner
如果你想知道是否有办法将字符串传递给函数并获取该字符串的对象表示形式,那么不,除了使用eval之外没有其他方法。 - David Titarenco

1

你不需要使用 eval。你可以直接传递对象而不是它的名称,并使用 var obj = new klass()。就这样

function factory(klass) {
  var obj = new klass();
  return obj;
}

function C() {...}
C.prototype = {...}

factory(C);

1
在我看来,他们想要传递一个变量的名称,一个字符串。这就是问题所在。 - katspaugh
@katspaugh 这个问题明确地说:“有没有更好(更安全,更干净,和/或更快)的方法来创建类而不使用eval?”阅读理解。 - Gabi Purcaru
然而,有一种方法可以通过提供名称和命名空间来完成类的创建,即括号表示法。而且它更接近原始函数,你不觉得吗? - katspaugh
@katspaugh 不,我不这么认为。原帖中没有声明 _namespace_。类定义可能在任何外部作用域级别中,所以括号表示法会失败。此外,如果您认为您有更好的解决方案,请发布一个答案;原帖作者可能会感激。 - Gabi Purcaru
更多信息:该字符串来自外部源,当工厂被调用时,不存在任何对象实例。 - Walt Stoneburner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接