在JavaScript中,我想创建一个对象实例(通过new操作符),但将任意数量的参数传递给构造函数。这可能吗?

我想做的是这样的(但下面的代码不起作用):

function Something(){
    // init stuff
}
function createSomething(){
    return new Something.apply(null, arguments);
}
var s = createSomething(a,b,c); // 's' is an instance of Something

这个问题的答案

从这里的响应可以清楚地看出,没有内置的方法可以使用new操作符调用.apply()。然而,人们对这个问题提出了许多非常有趣的解决方案。

我更喜欢的解决方案是来自Matthew Crumley的这个(我修改了它来传递arguments属性):

var createSomething = (function() {
    function F(args) {
        return Something.apply(this, args);
    }
    F.prototype = Something.prototype;

    return function() {
        return new F(arguments);
    }
})();

当前回答

这是一个通用的解决方案,可以调用任何构造函数(除了本机构造函数,当作为函数调用时行为不同,如String, Number, Date等)与参数数组:

function construct(constructor, args) {
    function F() {
        return constructor.apply(this, args);
    }
    F.prototype = constructor.prototype;
    return new F();
}

通过调用construct(Class,[1,2,3])创建的对象将与用new Class(1,2,3)创建的对象相同。

您还可以创建一个更具体的版本,这样就不必每次都传递构造函数。这也稍微更有效,因为它不需要每次调用时都创建内部函数的新实例。

var createSomething = (function() {
    function F(args) {
        return Something.apply(this, args);
    }
    F.prototype = Something.prototype;

    return function(args) {
        return new F(args);
    }
})();

这样创建和调用外部匿名函数的原因是为了防止函数F污染全局命名空间。它有时被称为模块模式。

(更新)

对于那些想在TypeScript中使用它的人,因为如果F返回任何东西,TS会给出一个错误:

function construct(constructor, args) {
    function F() : void {
        constructor.apply(this, args);
    }
    F.prototype = constructor.prototype;
    return new F();
}

其他回答

创建一个匿名原型,并使用参数将Something原型应用于它,然后创建该匿名原型的新实例。这样做的一个缺点是它不会通过s instanceof Something检查,尽管它是相同的,但它基本上是一个克隆的实例。

function Something(){
    // init stuff
}
function createSomething(){
    return new (function(){Something.apply(this, arguments)});
}
var s = createSomething(a,b,c); // 's' is an instance of Something
function createSomething() {
    var args = Array.prototype.concat.apply([null], arguments);
    return new (Function.prototype.bind.apply(Something, args));
}

如果您的目标浏览器不支持ECMAScript 5。绑定,代码将无法工作。这是不太可能的,但见兼容性表。

改良版的@Matthew的回答。这种形式具有将临时类存储在闭包中所获得的轻微性能优势,以及可以使用一个函数创建任何类的灵活性

var applyCtor = function(){
    var tempCtor = function() {};
    return function(ctor, args){
        tempCtor.prototype = ctor.prototype;
        var instance = new tempCtor();
        ctor.prototype.constructor.apply(instance,args);
        return instance;
    }
}();

这将通过调用applyCtor(类,[arg1, arg2, argn])来使用;

如果您的环境支持ECMA Script 2015的扩展操作符(…),您可以像这样简单地使用它

function Something() {
    // init stuff
}

function createSomething() {
    return new Something(...arguments);
}

注意:现在ECMA Script 2015的规范已经发布,大多数JavaScript引擎都在积极地实现它,这将是最好的方式。

您可以在这里查看在一些主要环境中对扩展操作符的支持。

这种构造函数方法在使用或不使用new关键字时都有效:

function Something(foo, bar){
  if (!(this instanceof Something)){
    var obj = Object.create(Something.prototype);
    return Something.apply(obj, arguments);
  }
  this.foo = foo;
  this.bar = bar;
  return this;
}

它假设支持Object。创建,但如果你支持旧的浏览器,你总是可以填充它。请参阅MDN上的支持表。

下面是一个JSBin,可以看到它在控制台输出中的作用。