1

I am trying to create a function which will take arguments arg1, arg2... then pass them into a constructor for a new object C like so: new C(arg1, arg2...), so to make a new instance of C the user would simply have to call C(arg) instead of new C(arg). Here is my first attempt:

var C = function(a){ this.a = a; }

var Cn = function(){
    new C.apply(this, arguments);
}

Cn(0)     // Should make a new C with a property a equal to 0
new C(0)  // ie the same as this

Edit: Note, I need it to take an arbitrary number of arguments and not use eval. I'm making a library implementing Algebraic Data Types in js.


Edit: The solution was to take Jeremy's Idea and adapt it to take an unbounded number of arguments:

var C = function() {

    // A unique object so we can identify when we used the 'newless' constructor
    var newlessConstructorObj = {}

    // Check to see if C has been called with `new`
    if(!(this instanceof C))
        // If not pass arguments as single arg back to C
        return new C(newlessConstructorObj, arguments);


    // Check to see if we got here from the line above, if so the arguments were passed in the second arg
    var args = (arguments[0] === newlessConstructorObj) ? arguments[1] : arguments

    // Do stuff with args
    this.a = args[0];
}

C(0);
new C(0);
Community
  • 1
  • 1
Callum Rogers
  • 15,630
  • 17
  • 67
  • 90

6 Answers6

2

If you want to be able to call the function with or without the new keyword, you have to follow this pattern:

C = function(a) {
  if (!(this instanceof C)) {
    return new C(a);
  }

  this.a = a;
}

so to create a new instance of "C":

c = new C(a);

and

c = C(a);

will both return a properly formed instance of C

Jeremy Hodge
  • 1,655
  • 9
  • 8
2

I would have picked Fabrizio Calderan's solution any day, but because you want this specific functionality - here is what predecessors tell us:


you can apply arguments to prototype.constructor (but there are issues when doing it with native types, like Number):

var Cn = function(){
    return C.prototype.constructor.apply(C, arguments);
}

Link: Instantiating a JavaScript object by calling prototype.constructor.apply


or.. use eval:

function construct(Constructor) 
{ 
 /* 
  * or Array.prototype.slice.call(arguments, 1).map(function() { ... }) 
  * in JavaScript 1.6+, compatibles, and with augmented Array.prototype 
  */ 
 var args = []; 
 for (var i = 1, len = arguments.length; i < len; i++) 
 { 
   args[i - 1] = "arguments[" + i + "]"; 
 } 

/* or args.join(", ") if you need it pretty-printed */ 
 return eval("new Constructor(" + args + ")"); 
} 

function Foo() 
{ 
  window.alert(Array.prototype.slice.call(arguments, 0).join(", ")); 
} 

var f = construct(Foo, /bar/g, {baz: 42});

Link: http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/ff1a104bdc33d5c8

Community
  • 1
  • 1
c69
  • 19,951
  • 7
  • 52
  • 82
  • 2
    I was playing around with `prototype.constructor` before I asked the question - in chrome `C.prototype.constructor.call(C, 0)` returns `undefined`. – Callum Rogers Apr 03 '12 at 14:19
1

something like this?

var C = function(obj){ 
  var a;
  for (a in obj) {
     this[a] = obj[a]; 
  }
}
var Cn = function(obj) { return new C(obj); }

instance = Cn({
  a : 1,
  b : 2
})  

instance.a //1
instance.b //2
instance.c //undefined
Fabrizio Calderan
  • 120,726
  • 26
  • 164
  • 177
  • It works, but I think the OP wanted to type less, not more. Consider replacing the object with an array (since the names aren't necessary anyway). – RobG Apr 03 '12 at 13:36
  • I just extended the example returning an instance of the object and grouping all arguments as values of an hashtable – Fabrizio Calderan Apr 03 '12 at 13:37
  • Unfortunately I can't change what the users passes so I can't use an object, however this has inspired me to a solution as I can change the "C" because the user never sees it (simply pass the `arguments` in `Cn` as an arg to `C` - I only iterate over `arguments` in `C` anyhow). – Callum Rogers Apr 03 '12 at 13:40
1

If you don't care about having a correct instanceof check you can try this:

var C = function(a){ this.a = a; }

var Cn = function(){
    return C.apply({}, arguments); // notice an empty object here
}

Cn(0)     // Should make a new C with a property a equal to 0
new C(0)  // ie the same as this
qwertymk
  • 34,200
  • 28
  • 121
  • 184
1

Doing it with a fixed number of args is simple:

// assuming one arg
function Cn(arg) {
  return new C(arg);
}

// assuming two args
function Cn(arg0, arg1) {
  return new C(arg0, arg1);
}

and so on. You can even make a general version for any number of parameters by iterating over arguments to create a string, then eval it. Crass, but effective.

But what's the point? Just to save typing 4 characters?

RobG
  • 142,382
  • 31
  • 172
  • 209
  • Ah, should have mentioned I need it for a general number of arguments and without using `eval`. As for the reason, I'm trying to make a functional programming library for js and I need this to implement [Algebraic Data Types](http://en.wikipedia.org/wiki/Algebraic_data_type). – Callum Rogers Apr 03 '12 at 13:37
0

Updated for ES6, you can use the spread operator:

var C = function(a){ this.a = a; }

var Cn = function(...args){
    return new C(...args);
}

assert.deepStrictEqual(Cn(10), new C(10));
pfg
  • 2,348
  • 1
  • 16
  • 37