How could I generalise the function below to take N arguments? (Using call or apply?)
Is there a programmatic way to apply arguments to 'new'? I don't want the constructor to be treated like a plain function.
/** * This higher level function takes a constructor and arguments * and returns a function, which when called will return the * lazily constructed value. * * All the arguments, except the first are pased to the constructor. * * @param {Function} constructor */ function conthunktor(Constructor) { var args = Array.prototype.slice.call(arguments, 1); return function() { console.log(args); if (args.length === 0) { return new Constructor(); } if (args.length === 1) { return new Constructor(args[0]); } if (args.length === 2) { return new Constructor(args[0], args[1]); } if (args.length === 3) { return new Constructor(args[0], args[1], args[2]); } throw("too many arguments"); } }
qUnit test:
test("conthunktorTest", function() { function MyConstructor(arg0, arg1) { this.arg0 = arg0; this.arg1 = arg1; } MyConstructor.prototype.toString = function() { return this.arg0 + " " + this.arg1; } var thunk = conthunktor(MyConstructor, "hello", "world"); var my_object = thunk(); deepEqual(my_object.toString(), "hello world"); });
Yes, it is possible, when your constructor function executes, the this value has already the [[Prototype]] internal property pointing to the ValidateFields.
It's not possible to call the constructor two times without using reflection as it's called only once to construct the object. Instance constructors are used to create and initialize any instance member variables when you use the new expression to create an object of a class.
JavaScript doesn't have function overloading, including for methods or constructors. If you want a function to behave differently depending on the number and types of parameters you pass to it, you'll have to sniff them manually.
This is how you do it:
function applyToConstructor(constructor, argArray) { var args = [null].concat(argArray); var factoryFunction = constructor.bind.apply(constructor, args); return new factoryFunction(); } var d = applyToConstructor(Date, [2008, 10, 8, 00, 16, 34, 254]);
Call is slightly easier
function callConstructor(constructor) { var factoryFunction = constructor.bind.apply(constructor, arguments); return new factoryFunction(); } var d = callConstructor(Date, 2008, 10, 8, 00, 16, 34, 254);
You can use either of these to create factory functions:
var dateFactory = applyToConstructor.bind(null, Date) var d = dateFactory([2008, 10, 8, 00, 16, 34, 254]);
or
var dateFactory = callConstructor.bind(null, Date) var d = dateFactory(2008, 10, 8, 00, 16, 34, 254);
It will work with any constructor, not just built-ins or constructors that can double as functions (like Date).
However it does require the Ecmascript 5 .bind function. Shims will probably not work correctly.
A different approach, more in the style of some of the other answers is to create a function version of the built in new
. This will not work on all builtins (like Date).
function neu(constructor) { // http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2 var instance = Object.create(constructor.prototype); var result = constructor.apply(instance, Array.prototype.slice.call(arguments, 1)); // The ECMAScript language types are Undefined, Null, Boolean, String, Number, and Object. return (result !== null && typeof result === 'object') ? result : instance; } function Person(first, last) {this.first = first;this.last = last}; Person.prototype.hi = function(){console.log(this.first, this.last);}; var p = neu(Person, "Neo", "Anderson");
And now, of course you can do .apply
or .call
or .bind
on neu
as normal.
For example:
var personFactory = neu.bind(null, Person); var d = personFactory("Harry", "Potter");
I feel that the first solution I give is better though, as it doesn't depend on you correctly replicating the semantics of a builtin and it works correctly with builtins.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With