Use of .apply() with 'new' operator. Is this possible?

后端 未结 30 3116
Happy的楠姐
Happy的楠姐 2020-11-22 00:39

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?

30条回答
  •  离开以前
    2020-11-22 00:57

    With ECMAScript5's Function.prototype.bind things get pretty clean:

    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
    }
    

    It can be used as follows:

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

    or even directly:

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

    This and the eval-based solution are the only ones that always work, even with special constructors like Date:

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

    edit

    A bit of explanation: We need to run new on a function that takes a limited number of arguments. The bind method allows us to do it like so:

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

    The anything parameter doesn't matter much, since the new keyword resets f's context. However, it is required for syntactical reasons. Now, for the bind call: We need to pass a variable number of arguments, so this does the trick:

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

    Let's wrap that in a function. Cls is passed as argument 0, so it's gonna be our anything.

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

    Actually, the temporary f variable is not needed at all:

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

    Finally, we should make sure that bind is really what we need. (Cls.bind may have been overwritten). So replace it by Function.prototype.bind, and we get the final result as above.

提交回复
热议问题