在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);
    }
})();

当前回答

任何函数(甚至是构造函数)都可以接受数量可变的参数。每个函数都有一个“arguments”变量,可以使用[].slice.call(arguments)转换为数组。

function Something(){
  this.options  = [].slice.call(arguments);

  this.toString = function (){
    return this.options.toString();
  };
}

var s = new Something(1, 2, 3, 4);
console.log( 's.options === "1,2,3,4":', (s.options == '1,2,3,4') );

var z = new Something(9, 10, 11);
console.log( 'z.options === "9,10,11":', (z.options == '9,10,11') );

上述测试产生以下输出:

s.options === "1,2,3,4": true
z.options === "9,10,11": true

其他回答

改良版的@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])来使用;

如果您对基于求值的解决方案感兴趣

function createSomething() {
    var q = [];
    for(var i = 0; i < arguments.length; i++)
        q.push("arguments[" + i + "]");
    return eval("new Something(" + q.join(",") + ")");
}

其实最简单的方法是:

function Something (a, b) {
  this.a = a;
  this.b = b;
}
function createSomething(){
    return Something;
}
s = new (createSomething())(1, 2); 
// s == Something {a: 1, b: 2}

作为一个迟来的回答,我想我只是把它放在这里作为一个更完整的解决方案,使用了这里已经概述的许多原则。

Implements.js

首先,这里有一个基本用法:

var a = function(){
    this.propa = 'a';
}
var b = function(){
    this.propb = 'b'
}
var c = Function.Implement(a, b); // -> { propa: 'a', propb: 'b' }

在ECMAScript5的Function.prototype.bind中,事情变得非常清晰:

function newCall(Cls) {
    return new (Function.prototype.bind.apply(Cls, arguments));
    // or even
    // return new (Cls.bind.apply(Cls, arguments));
    // if you know that Cls.bind has not been overwritten
}

它可以这样使用:

var s = newCall(Something, a, b, c);

或者直接说:

var s = new (Function.prototype.bind.call(Something, null, a, b, c));

var s = new (Function.prototype.bind.apply(Something, [null, a, b, c]));

这和基于求值的解决方案是唯一总是有效的,即使使用像Date这样的特殊构造函数:

var date = newCall(Date, 2012, 1);
console.log(date instanceof Date); // true

edit

稍微解释一下: 我们需要在一个接受有限数量参数的函数上运行new。bind方法允许我们这样做:

var f = Cls.bind(anything, arg1, arg2, ...);
result = new f();

any形参并不重要,因为new关键字会重置f的上下文。但是,由于语法原因,它是必需的。现在,对于bind调用:我们需要传递一个可变数量的参数,这样就可以了:

var f = Cls.bind.apply(Cls, [anything, arg1, arg2, ...]);
result = new f();

让我们把它包装在一个函数中。Cls被作为参数0传递,所以它是我们的任何东西。

function newCall(Cls /*, arg1, arg2, ... */) {
    var f = Cls.bind.apply(Cls, arguments);
    return new f();
}

实际上,临时变量f根本不需要:

function newCall(Cls /*, arg1, arg2, ... */) {
    return new (Cls.bind.apply(Cls, arguments))();
}

最后,我们应该确保bind确实是我们所需要的。(Cls。Bind可能已经被覆盖)。所以用function。prototype替换它。绑定,我们得到如上所示的最终结果。