views:

404

answers:

3

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?

+2  A: 

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);
CMS
+1, fantastic thanks
Fire Crow
A: 

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]);
// more or less equivalent to
var f2 = new Foo(2, 3);

The construct() function:

function construct(klass, args) {

  function F() {
    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();
mjs
+1  A: 

A better solution is to create a temporary constructor function, apply the prototype of the class that you want (to ensure prototype chains are preserved) and then apply the constructor manually. This prevents calling the constructor twice unnecessarily...

applyCtor = function(ctor,args){
    function tempCtor() {};
    tempCtor.prototype = ctor.prototype;
    var instance = new tempCtor();
    ctor.prototype.constructor.apply(instance,args);
    return instance;
};
I would quantify that its better only in the case that the "ctor" function is expensive. reason being, that this implimentation involves calling two functions as well, just that tempCtor is an empty function. The apply call to ctor (ctor.apply) would execute as a normal function, only with the new keyword would it have the overhead of constructing a new object.
Fire Crow