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

后端 未结 30 2687
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:06

    Solution without ES6 or 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);
    


    output

    Demo {arg1: "X", arg2: "Y", arg3: "Z"}


    ... or "shorter" way:

    var func = new Function("return function " + Demo.name + "() { };")();
    func.prototype = Demo.prototype;
    var obj = new func();
    
    Demo.apply(obj, ["X", "Y", "Z"]);
    


    edit:
    I think this might be a good solution:

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

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

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

    An improved version of @Matthew's answer. This form has the slight performance benefits obtained by storing the temp class in a closure, as well as the flexibility of having one function able to be used to create any class

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

    This would be used by calling applyCtor(class, [arg1, arg2, argn]);

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

    See also how CoffeeScript does it.

    s = new Something([a,b,c]...)

    becomes:

    var s;
    s = (function(func, args, ctor) {
      ctor.prototype = func.prototype;
      var child = new ctor, result = func.apply(child, args);
      return Object(result) === result ? result : child;
    })(Something, [a, b, c], function(){});
    
    0 讨论(0)
  • 2020-11-22 01:08

    You can't call a constructor with a variable number of arguments like you want with the new operator.

    What you can do is change the constructor slightly. Instead of:

    function Something() {
        // deal with the "arguments" array
    }
    var obj = new Something.apply(null, [0, 0]);  // doesn't work!
    

    Do this instead:

    function Something(args) {
        // shorter, but will substitute a default if args.x is 0, false, "" etc.
        this.x = args.x || SOME_DEFAULT_VALUE;
    
        // longer, but will only put in a default if args.x is not supplied
        this.x = (args.x !== undefined) ? args.x : SOME_DEFAULT_VALUE;
    }
    var obj = new Something({x: 0, y: 0});
    

    Or if you must use an array:

    function Something(args) {
        var x = args[0];
        var y = args[1];
    }
    var obj = new Something([0, 0]);
    
    0 讨论(0)
提交回复
热议问题