Reputation: 153184
I have written the following function that permits creation of singleton classes from given classes:
function SingletonFrom(Constructor) {
return function() {
var self = arguments.callee;
if (self._instance === undefined) {
switch (arguments.length) { // this is ugly
case 0: self._instance = new Constructor(); break;
case 1: self._instance = new Constructor(arguments[0]); break;
case 2: self._instance = new Constructor(arguments[0], arguments[1]); break;
// [...]
case 10: // ...
default: throw new Error('Error in Singleton: Constructor may take at most 10 arguments'); break;
}
}
return self._instance;
}
}
Example:
var Array_Singleton = new SingletonFrom(Array);
var x = new Array_Singleton(1,2,3); // [1,2,3]
var y = new Array_Singleton(0,0,0,0); // [1,2,3]
alert(x === y); // true
It works great, however I'm not quite happy with the switch
statement. The problem is passing a variable number of arguments to a constructor called with the "new
" keyword is not possible. So, my Constructor function must not take more than 10 arguments. For example, this will fail:
new Array_Singleton(1,2,3,4,5,6,7,8,9,10,11);
Any way to work around this?
Upvotes: 0
Views: 502
Reputation: 39188
Javascript has this annoying limitation of not being able to call constructor with an array as a list of arguments. What's usually accomplished with Function.prototype.apply
when calling function as a function (e.g. foo(...)
), can't be easily applied to a function when it's called as constructor (e.g. new foo(...)
).
I'm guessing this is exactly why you resort to switch
there.
function foo(a,b) {
return a+b;
}
foo.apply(null, [1,2]); // 3
But not so easy with constructor:
function Person(fname, lname) {
this.fname = fname;
this.lname = lname;
}
Person.prototype.speak = function() {
return 'Hi. My name is: ' + this.fname + ' ' + this.lname;
}
new Person.apply(null, ['John', 'Appleseed']); // doesn't work!
To work around that, you can create a simple helper which would practically simulate what new
does, only this time with apply
. The algorithm is straight-forward:
apply
to call constructor in context of this newly created object, passing it list of arguments with apply
.It would look something like this:
function newApply(ctr, array) {
function F(){}
F.prototype = ctr.prototype;
var obj = new F();
ctr.apply(obj, array);
return obj;
}
newApply(Person, ['John', 'Appleseed']); // returns new object
Alternatively, you could avoid F
object creation at run time to save on performance and memory consumption:
var newApply = (function(){
function F(){}
return function(ctr, array) {
F.prototype = ctr.prototype;
var obj = new F();
ctr.apply(obj, array);
return obj;
}
})();
Upvotes: 1
Reputation: 245479
Off the top of my head, I can only think of one and I probably wouldn't recommend it without an extremely hefty amount of testing.
That being said; instead of passing in your parameters seperately, pass them as an array. You can then iterate through the array and build a string with your call to new
. Once you have your string, you can call eval()
to run your generated command.
That will give you a way to handle any dynamic number of parameters.
Upvotes: 0