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

当前回答

从ES6开始,这可以通过扩展操作符实现,请参见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Apply_for_new

这个答案已经在https://stackoverflow.com/a/42027742/7049810评论中给出了,但似乎被大多数人忽略了

其他回答

这个答案有点晚了,但我想任何看到这个的人都可以使用它。有一种方法可以使用apply返回一个新对象。尽管它需要对对象声明做一点更改。

function testNew() {
    if (!( this instanceof arguments.callee ))
        return arguments.callee.apply( new arguments.callee(), arguments );
    this.arg = Array.prototype.slice.call( arguments );
    return this;
}

testNew.prototype.addThem = function() {
    var newVal = 0,
        i = 0;
    for ( ; i < this.arg.length; i++ ) {
        newVal += this.arg[i];
    }
    return newVal;
}

testNew( 4, 8 ) === { arg : [ 4, 8 ] };
testNew( 1, 2, 3, 4, 5 ).addThem() === 15;

为了让第一个if语句在testNew中工作,你必须返回这个;在函数的底部。以代码为例:

function Something() {
    // init stuff
    return this;
}
function createSomething() {
    return Something.apply( new Something(), arguments );
}
var s = createSomething( a, b, c );

更新:我改变了我的第一个例子的总和参数的数量,而不是只有两个。

创建一个匿名原型,并使用参数将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

虽然其他方法是可行的,但它们过于复杂。在Clojure中,您通常创建一个实例化类型/记录的函数,并使用该函数作为实例化的机制。翻译成JavaScript:

function Person(surname, name){
  this.surname = surname;
  this.name = name;
}

function person(surname, name){ 
  return new Person(surname, name);
}

通过采用这种方法,可以避免使用上面描述的new except。当然,这个函数与apply或任何其他函数式编程特性一起工作都没有问题。

var doe  = _.partial(person, "Doe");
var john = doe("John");
var jane = doe("Jane");

通过使用这种方法,您的所有类型构造函数(例如Person)都是普通的、不做任何事情的构造函数。您只需传入参数并将它们分配给同名的属性。麻烦的细节放在构造函数中(例如person)。

创建这些额外的构造函数没有什么麻烦,因为它们是一种很好的实践。它们很方便,因为它们允许您潜在地拥有几个具有不同细微差别的构造函数。

这种构造函数方法在使用或不使用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,可以看到它在控制台输出中的作用。

修改了@Matthew的答案。在这里,我可以像往常一样传递任意数量的参数给函数(不是数组)。此外,'Something'也没有硬编码:

function createObject( constr ) {   
  var args =  arguments;
  var wrapper =  function() {  
    return constr.apply( this, Array.prototype.slice.call(args, 1) );
  }

  wrapper.prototype =  constr.prototype;
  return  new wrapper();
}


function Something() {
    // init stuff
};

var obj1 =     createObject( Something, 1, 2, 3 );
var same =     new Something( 1, 2, 3 );