动态对象创建

3

我有一个函数,它接受一个字符串对象名称,我需要函数创建一个与该字符串值相同名称的新对象实例。

例如:

function Foo(){}
function create(name){
    return new name();
}
create('Foo'); //should be equivalent to new Foo();

虽然我知道可以通过eval实现,但尽量避免使用它。如果有人对以下问题有替代方案,我也很感兴趣。


我有一个数据库和一组(使用经典的面向对象方法)类,大致上为每个表定义了通用操作。(与使用PHP的Zend_Db非常相似)。由于一切都是异步的,基于上一个任务结果执行任务可能会导致代码缩进很深。

var table1 = new Table1Db();
table1.doFoo({
    success:function(){
        var table2 = new Table2Db();
        table2.doBar({
            notFound:function(){
                doStuff();
            }
        });
    }
});

显而易见的解决方案是创建帮助方法,抽象代码的异步性。
Db.using(db) //the database object
  .require('Table1', 'doFoo', 'success') //table name, function, excpected callback
  .require('Table2', 'doBar', 'notFound')
  .then(doStuff);

这样会简化问题。但是问题在于,我需要能够创建表类,其名称可以从传递给 require 的第一个参数中推断出来,这就导致了上述问题...

2个回答

4
为什么不直接将构造函数传递到require方法中呢?这样你就可以避开从名称转换为函数的整个问题。那么你的示例将如下所示:
Db.using(db) //the database object
  .require(Table1Db, 'doFoo', 'success') //table constructor, function name, expected callback
  .require(Table2Db, 'doBar', 'notFound')
  .then(doStuff);

然而,如果你真的想使用字符串...
你为什么一定要避免使用eval呢?它是语言中的一个工具,每个工具都有其用途(就像每个工具都可能被滥用)。如果你担心允许任意执行,那么一个简单的正则表达式测试应该可以使你的使用变得安全。
如果你一定要避免使用eval,并且如果你所有的构造函数都是在默认的全局作用域中创建的(即window对象),那么这将起作用:
function create(name) {
  return new window[name]();
}

如果你想更加高级并支持命名空间对象(例如create('MyCompany.MyLibrary.MyObject')),可以像下面这样做:

function create(name) {
  var current,
      parts,
      constructorName;

  parts = name.split('.');
  constructorName = parts[parts.length - 1];
  current = window;
  for (var i = 0; i < parts.length - 1; i++) {
    current = current[parts[i]];
  }

  return new current[constructorName]();
}

1
首先,感谢您的回复。像往常一样,在查看答案后,我感到有些愚笨,因为我错过了一些(回想起来相当显然的)事情。在这种情况下,字符串更容易,但是传递构造函数(我不知道可以以那种方式进行)看起来更整洁,更重要的是更清晰。我并不完全反对使用eval,并且正如您所说,它也有其用处,但是经验告诉我,如果我认为自己需要使用它,99%的时间我还不够了解该语言。(这就是其中之一)。 - Yacoby
@Yacoby 说得好! :) 特别是最后一句话。我希望每个人都能这样思考。 - gblazex
这是一个很棒的答案。学到了一些关于JavaScript的新知识!谢谢 :) - rossipedia

2
你曾经站在完整性的门口。虽然Annabelle的解决方案让你以你想要的方式做你刚才想做的事情(传递字符串),但是让我给你提供一种替代方法(传递函数引用)。
function Foo(){}
function create(name){
    return new name();
}
create(Foo); // IS equivalent to new Foo();

并且哇,它起作用了 :) 我告诉过你。你已经站在解决方案的门口。

发生的事情是你试图这样做

new 'Foo'()

这似乎没有太多意义,不是吗?但现在你通过引用传递函数,所以行 return new name(); 将被转换为 return new Foo(); ,就像你期望的那样。
现在,您可以抽象化应用程序的异步性。 玩得开心!
附录: 函数是一等对象,这意味着它们可以通过引用存储,作为参数通过引用传递或由另一个函数返回值。

require()的第二个和第三个参数仍然必须是字符串,它们所标识的对象是尚不存在的对象的属性。 - Annabelle

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