Use of .apply() with 'new' operator. Is th

2018-12-31 03:11发布

In JavaScript, I want to create an object instance (via the new operator), but pass an arbitrary number of arguments to the constructor. Is this possible?

What I want to do is something like this (but the code below does not work):

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

The Answer

From the responses here, it became clear that there's no built-in way to call .apply() with the new operator. However, people suggested a number of really interesting solutions to the problem.

My preferred solution was this one from Matthew Crumley (I've modified it to pass the arguments property):

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

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

30条回答
浮光初槿花落
2楼-- · 2018-12-31 03:47

Actually the simplest method is:

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}
查看更多
余生无你
3楼-- · 2018-12-31 03:47

A revised solution from @jordancpaul's answer.

var applyCtor = function(ctor, args)
{
    var instance = new ctor();
    ctor.prototype.constructor.apply(instance, args);
    return instance;
}; 
查看更多
妖精总统
4楼-- · 2018-12-31 03:51

if you're interested in an eval-based solution

function createSomething() {
    var q = [];
    for(var i = 0; i < arguments.length; i++)
        q.push("arguments[" + i + "]");
    return eval("new Something(" + q.join(",") + ")");
}
查看更多
皆成旧梦
5楼-- · 2018-12-31 03:51

This works!

var cls = Array; //eval('Array'); dynamically
var data = [2];
new cls(...data);
查看更多
泛滥B
6楼-- · 2018-12-31 03:53

Suppose you've got an Items constructor which slurps up all the arguments you throw at it:

function Items () {
    this.elems = [].slice.call(arguments);
}

Items.prototype.sum = function () {
    return this.elems.reduce(function (sum, x) { return sum + x }, 0);
};

You can create an instance with Object.create() and then .apply() with that instance:

var items = Object.create(Items.prototype);
Items.apply(items, [ 1, 2, 3, 4 ]);

console.log(items.sum());

Which when run prints 10 since 1 + 2 + 3 + 4 == 10:

$ node t.js
10
查看更多
几人难应
7楼-- · 2018-12-31 03:53

In ES6, Reflect.construct() is quite convenient:

Reflect.construct(F, args)
查看更多
登录 后发表回答