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

后端 未结 30 2712
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 01:01

    since ES6 this is possible through the Spread operator, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Apply_for_new

    This answer was already, sort of given in comment https://stackoverflow.com/a/42027742/7049810, but seems to have been missed by most

    0 讨论(0)
  • 2020-11-22 01:02

    This constructor approach works both with and without the new keyword:

    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;
    }
    

    It assumes support for Object.create but you could always polyfill that if you're supporting older browsers. See the support table on MDN here.

    Here's a JSBin to see it in action with console output.

    0 讨论(0)
  • 2020-11-22 01:03

    I just came across this problem, and I solved it like this:

    function instantiate(ctor) {
        switch (arguments.length) {
            case 1: return new ctor();
            case 2: return new ctor(arguments[1]);
            case 3: return new ctor(arguments[1], arguments[2]);
            case 4: return new ctor(arguments[1], arguments[2], arguments[3]);
            //...
            default: throw new Error('instantiate: too many parameters');
        }
    }
    
    function Thing(a, b, c) {
        console.log(a);
        console.log(b);
        console.log(c);
    }
    
    var thing = instantiate(Thing, 'abc', 123, {x:5});
    

    Yeah, it's a bit ugly, but it solves the problem, and it's dead simple.

    0 讨论(0)
  • 2020-11-22 01:03

    Make an anonymous prototype and apply the Something prototype to it using the arguments and then create a new instance of that anonymous prototype. The one disadavantage of this is it will not pass the s instanceof Something check, though it is identical, it is basically an instance of a clone.

    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
    
    0 讨论(0)
  • 2020-11-22 01:04

    This answer is a little late, but figured anyone who sees this might be able to use it. There is a way to return a new object using apply. Though it requires one little change to your object declaration.

    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;
    

    For the first if statement to work in testNew you have to return this; at the bottom of the function. So as an example with your code:

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

    Update: I've changed my first example to sum any number of arguments, instead of just two.

    0 讨论(0)
  • 2020-11-22 01:05

    Here is my version of createSomething:

    function createSomething() {
        var obj = {};
        obj = Something.apply(obj, arguments) || obj;
        obj.__proto__ = Something.prototype; //Object.setPrototypeOf(obj, Something.prototype); 
        return o;
    }
    

    Based on that, I tried to simulate the new keyword of JavaScript:

    //JavaScript 'new' keyword simulation
    function new2() {
        var obj = {}, args = Array.prototype.slice.call(arguments), fn = args.shift();
        obj = fn.apply(obj, args) || obj;
        Object.setPrototypeOf(obj, fn.prototype); //or: obj.__proto__ = fn.prototype;
        return obj;
    }
    

    I tested it and it seems that it works perfectly fine for all scenarios. It also works on native constructors like Date. Here are some tests:

    //test
    new2(Something);
    new2(Something, 1, 2);
    
    new2(Date);         //"Tue May 13 2014 01:01:09 GMT-0700" == new Date()
    new2(Array);        //[]                                  == new Array()
    new2(Array, 3);     //[undefined × 3]                     == new Array(3)
    new2(Object);       //Object {}                           == new Object()
    new2(Object, 2);    //Number {}                           == new Object(2)
    new2(Object, "s");  //String {0: "s", length: 1}          == new Object("s")
    new2(Object, true); //Boolean {}                          == new Object(true)
    
    0 讨论(0)
提交回复
热议问题