javascript apply on constructor, throwing "malformed formal parameter"

Viewed 6059

thanks to wonderful responses to this question I understand how to call javascript functions with varargs.

now I'm looking to use apply with a constructor

I found some interesting information on this post.

but my code is throwing errors

attempt 1:

var mid_parser = new Parser.apply(null, mid_patterns);

error:

TypeError: Function.prototype.apply called on incompatible [object Object]

attempt 2: attempt 1:

var mid_parser = new Parser.prototype.apply(null, mid_patterns);

error:

TypeError: Function.prototype.apply called on incompatible [object Object]

attempt 2:

function Parser()
{
    this.comparemanager = new CompareManager(arguments);
}

mid_patterns = [objA,objB,objC]
var mid_parser = new Parser();
Parser.constructor.apply(mid_parser, mid_patterns);

error:

syntax_model.js:91: SyntaxError: malformed formal parameter

attempt 3:

var mid_parser = Parser.apply(null, mid_patterns);

error :

TypeError: this.init is undefined // init is a function of Parser.prototype

I have a workaround for the time being:

function Parser()
{
    if(arguments.length) this.init.call(this,arguments); // call init only if arguments
}
Parser.prototype = {
   //...
   init: function()
   {
         this.comparemanager = new CompareManager(arguments);
   }
   //...
}

var normal parser = new Parser(objA,objB,objC);

mid_patterns = [objA,objB,objC]
var dyn_parser = new Parser();
dyn_parser.init.apply(dyn_parser, mid_patterns);

this works pretty well, but it's not as clean and universal as I'd like.

is it possible in javascript to call a constructor with varargs?

4 Answers

You could use apply and pass an empty object as the this argument:

var mid_parser = {};
Parser.apply(mid_parser, mid_patterns);

But that solution will not take care about the prototype chain.

You could create a Parser object, using the new operator, but without passing arguments, and then use apply to re-run the constructor function:

var mid_parser = new Parser();
Parser.apply(mid_parser, mid_patterns);

You can exploit the fact that you can chain constructors using apply(...) to achieve this, although this requires the creation of a proxy class. The construct() function below lets you do:

var f1 = construct(Foo, [2, 3]);
// which is more or less equivalent to
var f2 = new Foo(2, 3);

The construct() function:

function construct(klass, args) {

  function F() {
    return klass.apply(this, arguments[0]); 
  }; 

  F.prototype = klass.prototype; 

  return new F(args);

}

Some sample code that uses it:

function Foo(a, b) {
  this.a = a; this.b = b;
}

Foo.prototype.dump = function() {
  console.log("a = ", this.a);
  console.log("b = ", this.b);
};

var f = construct(Foo, [7, 9]);

f.dump();
Related