'new' 'Factory' with variable arguments

Let me first explain the issue piecemeal.

I need to create a constructor function based on the “Factory pattern” – meaning I don’t want to use the ‘new’ operator when I call this constructor. It’s easy, for example…

function Make(x) {
if ( !(this instanceof arguments.callee) )
  return new arguments.callee(x);

  // do your stuff...
}

BUT, I need this method to accept variable no. of arguments, like this…

function Take(/* ... */) {
  // process arguments with 'arguments[]' array-like object

  // do your stuff...
}

Now, the PROBLEM is how do i make a constructor that implements the ‘Factory pattern’ AND accepts variable no. of arguments.

If you do this…

function Fake(/* ... */) {
if ( !(this instanceof arguments.callee) )
  return new arguments.callee.apply(null, arguments);

  // do your stuff...
}

You’ll get an error telling you that apply() method is NOT a constructor. The porblem here is that the ‘new object’ is being passed to apply() method, not to our constructor.

=== My Solution[s] ===

I came up with a couple of solutions…

The first is to use the eval() function to dynamically create JavaScript code that calls the constructor.

Function Shake(/* ... */) {
  if ( !(this instanceof arguments.callee) ) {
    // collect all the arguments
    var arr = [];
    for ( var i = 0; arguments[i]; i++)
      arr.push( 'arguments[' + i + ']' );
			
    // create code
    var code = 'new arguments.callee(' + arr.join(',') + ');';
    
    // call it
    return eval( code );
  }

  // do your stuff with variable arguments...
}

The other solution is to use the proto property of an ‘empty’ object ({}) to change its constructor.

Every object has this proto property which is a ‘secret’ link to its prototype object. “Fortunately” this property is writable.

The solution works like this…

function Cake(/*...*/) {
  var obj = {};
		
  // do your stuff on 'obj'
  // just like you'd do on 'this'
		
		
  // now do the __proto__ magic
  // 'mutate' obj to make it a different object
  
  obj.__proto__ = arguments.callee.prototype;

  // after the above change, the following is true -- which is what we need
  // obj instanceof arguments.callee;
  
  // return obj
  return obj;
}

Here are some questions I’d like to be answered
[Q] Which method is better? Obviously I had to ask this :wink: – I like second.

[Q] Is the second method right? It’s worked for me, but haven’t tested it thoroughly.

[Q] Is the second method ‘non-standard’ – meaning, is proto an internal
implementation detail that should not be relied on?

[Q] Even if proto is internal, can we safely assume that every JavaScript engine (at least all mainstream ones) have it implemented uniformly, and will
not REMOVE or CHANGE it in future?

+++Thanks+++

Regards first method, rather than me spouting a load of nonsense, probably worth googling ‘Eval evil’.

Regards the second method interesting that proto is writable.

Therefore you could drop the constructor and just use literals.

var methods = {area : function(){alert (this.x * this.y)}}
var obj = {x: 1, y: 2}
obj.__proto__ = methods;

obj.area();

Unfortunately it isn’t a standard and won’t work in IE. There’s also probably some other good reasons why this should be avoided. Looks far too easy to me:)

The problem I’m guessing is with passing arguments? You need ‘apply’ for that, otherwise you end up with one argument which is an array.

So rather than being able to say this.x = x, you have to do something like this.x = arguments[0][0].

Alternatively couldn’t you pass in an object instead?

Like this

function Make(o) {
  if ( !(this instanceof Make) ){
	return new Make(o);
  }
  this.x = o.x || 0;
  this.y = o.y || 0;
  this.z = o.z || 0;
}

var obj = Make ({x: 1, y: 2, z: 3});

Just a thought.

RLM

B…I…N…G…O…!

I got yet another solution – and this time it works in IE too.

Let me rephrase the problem first…

I need a constructor called ‘List’ that can accept variable no. of arguments. It’s easy to do this:

function List() {
  var arr = [];
  for ( var i = 0; arguments[i]; i++)
    arr.push( arguments[i] );

  this.arr = arr;
}

Now when I need to call this constructor, I must do it this way…

var ls1 = new List(1,2,3,4,5);
var ls2 = new List("apple", "google", "oracle");
// and so on...

The problem is that I need to avoid having to prefix calls to List() with ‘new’ – I want to be able to get the new object (of type List) returned without using ‘new’ on my part.

The first immediate solution seems to be the ‘apply’ method like this:

function List() {
  if ( !(this instanceof arguments.callee) )
    return new arguments.callee.apply(null, arguments);
 // ...
}

This is WRNOG however – the new object is passed to the apply method and NOT to our method (arguments.callee).

Earlier I presented two solutions. The eval() solution seems clumsy and comes with all the problems of “evil eval”. On the other hand proto solution is non-standard and the “Great Browser of MiSery” doesn’t honor it.

Here is another one.

// first write your "main" constructor function like this:

function List() {
  // this line is important
  var obj = new arguments.callee._construct();
  
  // now do your stuff on 'obj' just like you'd do on 'this'
  var arr = [];
  for ( var i = 0; arguments[i]; i++)
    arr.push( arguments[i]*i );
  obj.arr = arr;
		
  // this is necessary -- you have to return obj
  return obj;
}

// now first write the _construct function -- is it simple? NO
List._construct = function() {};

// this line does all the *magic*...
List._construct.prototype = List.prototype;

That’s it. Now we can do these…

var ls1 = List(1,2,3,4,5);

ls1 instanceof List;	// true (in IE also)

List.prototype.toString = function() {
  return this.arr.join("::");
};

ls1 + "";	// 1::2::3::4::5

Please comment on this one.
thanks.

I have to leave now, but does Crockford’s Act III: Function the Ultimate have anything useful to add?

Here are the slides. Page 54 shows a new_constructor function that looks to be useful (demo’d on page 53) followed up by module patterns, and power constructors.

@RLM2008

There’s also probably some other good reasons why this should be avoided. Looks far too easy to me…

I’d like to know what other reasons might obviate it’s use – especially on SSJS platforms, where there is hopefully no IE lurking around and you only
have to target your code to one JavaScript engine.

Q: IfF, we can somehow ascertain that proto won’t be CHANGED/RENAMED in near forseeable future (atleast in SpiderMonkey/TraceMonkey and v8) can’t we use it without the fear of it breaking our code?

:frowning::slight_smile:

I’m only learning this stuff myself, so maybe I’m missing the point of your pattern here. This seems to do the same.

function List(){
  var args = [].slice.call(arguments), // return arguments as a proper array
  F = function(){ this.arr = args; };
  F.prototype = List.prototype;
  return new F();
}

List.prototype.toString = function() {
  return this.arr.join("::");
};

var ls1 = List(1,2,3,4,5);

console.log (ls1 + ""); // 1::2::3::4::5

RLM

@pmw57

…does Crockford’s Act III: Function the Ultimate have anything useful to add…

YES. The ‘new_constructor’ function (slide 54) is pertty must what I have in my “third solution”. And, new_constructor() has bigger infrastructure and more flexibility.

So I think Crockford’s ‘new_constructor’ (or my third solution) is the way to go. But I still have that nagging question unanswered:

Isn’t proto method OK – at least where IE is not present (SSJS) – and if not why???

Some links on proto

http://ejohn.org/blog/objectgetprototypeof/

Note:
The Mozilla Reference says:

“This property is deprecated and should not be used in new code: use Object.getPrototypeOf instead”

But Object.getPrototypeOf() does not let you SET the prototype, and there is no corresponding Object.setPrototypeOf() method – which leaves us with the same problem.

:frowning: —> :expressionless: —> :slight_smile:

@RLM2008

… I’m missing the point of your pattern …

The gist of this thread is to find an optimal way to create a constructor that:

[1] Accepts variable no. of arguments, AND,
[2] DOESN’T need ‘new’ on it’s invocation.

And I think we’ve found that:
Crockford’s ‘new_constructor’ function (slide 54):
http://www.slideshare.net/douglascrockford/crockford-on-javascript-act-iii-function-the-ultimate

or my third solution:

(although newer ideas are welcome :))

…This seems to do the same…

Your implementation some problems…

You are creating a closure everytime you imvoke List() – that is way too inefficient – eval() is far better then.

Also we don’t need to do the following inside List():

F.prototype = List.prototype;

If you make F() a static method of List() – you can get away with doing it just once outside.

You don’t have to flesh out the object inside F() either –

F = function(){ this.arr = args; };

just get a new object through F() – so that when we make F’s prototype point to List’s prototype, we get the following to be true:

ls1 instanceof List;	// true

Happy JS.

  var arr = [];
  for ( var i = 0; arguments[i]; i++)
    arr.push( arguments[i]*i );
  obj.arr = arr;
       
  // this is necessary -- you have to return obj
  return obj; 

That’s creating a closure, surely? Your’re returning obj outside of the function which forms a permanent reference to the variable ‘arr’ inside the function.

No different as far as I can tell.

Also I don’t see how eval is favourable to using closures. Watch that crockford video and you’ll see he utilises closures a great deal. As far as I gather they are one of the strong points of javascript.

Also we don’t need to do the following inside List():

So what about this bit of prototypal inheritence code

    function object(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }

The whole point is that you don’t tag on the prototype code outside of the function. This is covered in the Crockford video.

I’d admit I’ve got a lot to learn, but I’m sorry I’m not totally convinced by your arguments. Especially the eval comparison.

RLM

@RLM2008

…That’s creating a closure, surely?..

That’s NOT a closure – check your sources.

…I don’t see how eval is favorable to using closures…

Closures are not the problem – problem is that you create a lot of them in your code – one for each List() object – that’s far less acceptable than eval() trickery.

Anyway the problem is pretty much solved … thanks for all responses.

Happy JS :slight_smile:

That’s NOT a closure – check your sources.

Yes, I don’t know where I got that idea from. Maybe mixing it up with returning an object and it’s methods.

Mind’s on work at the moment, so not really focused.

RLM