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

当前回答

Matthew Crumley在CoffeeScript中的解决方案:

construct = (constructor, args) ->
    F = -> constructor.apply this, args
    F.prototype = constructor.prototype
    new F

or

createSomething = (->
    F = (args) -> Something.apply this, args
    F.prototype = Something.prototype
    return -> new Something arguments
)()

其他回答

不含ES6或polyfills的解决方案:

var obj = _new(Demo).apply(["X", "Y", "Z"]);


function _new(constr)
{
    function createNamedFunction(name)
    {
        return (new Function("return function " + name + "() { };"))();
    }

    var func = createNamedFunction(constr.name);
    func.prototype = constr.prototype;
    var self = new func();

    return { apply: function(args) {
        constr.apply(self, args);
        return self;
    } };
}

function Demo()
{
    for(var index in arguments)
    {
        this['arg' + (parseInt(index) + 1)] = arguments[index];
    }
}
Demo.prototype.tagged = true;


console.log(obj);
console.log(obj.tagged);

输出 演示{arg1: "X", arg2: "Y", arg3: "Z"} …或者“更短”的方式:

var func = new Function("return function " + Demo.name + "() { };")();
func.prototype = Demo.prototype;
var obj = new func();

Demo.apply(obj, ["X", "Y", "Z"]);

编辑: 我认为这可能是一个很好的解决方案:

this.forConstructor = function(constr)
{
    return { apply: function(args)
    {
        let name = constr.name.replace('-', '_');

        let func = (new Function('args', name + '_', " return function " + name + "() { " + name + "_.apply(this, args); }"))(args, constr);
        func.constructor = constr;
        func.prototype = constr.prototype;

        return new func(args);
    }};
}

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

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

这是一个通用的解决方案,可以调用任何构造函数(除了本机构造函数,当作为函数调用时行为不同,如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();
}

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

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

Implements.js

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

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