Wednesday, 23 June 2010

Singletonize

Since I wrote this post I had in mind if it would be possible to transform a normal "class" in a singleton "class" (I'm thinking in JavaScript here, that's why I'm quoting "class"). Well, maybe it does not look much useful, but it sounds freak enough to be appealing to me.

In principle it didn't seem too complex, but when I started to write it, I found some problems.

Updated on 2011/06/23
Most of what you can read after this paragraph is misleading and based on some misconptions that I had at the time... just jump to this new article.
Anyway, I leave this old article here for historical reasons (and for my own shame :-)



The idea is storing the initial "constructor" somewhere, and return a new "constructor" function that checks whether the singleton instance has been created or not, if not, it calls the initial "constructor".
That's the main problem, invoking a function as a constructor (that is, with new before) is not that easy as invoking a normal function with Function.prototype.apply(...) or Function.prototype.call(...)

I mean, when we invoke:

initialFunc.apply("this", "arguments");

that "this" should be the new object that in a normal invocation (new intialFunction(...);) the new statement would have just created...

I searched the net for some help and in principle it seems like there's not a clear way to do it, except using the almighty eval().

I already knew that eval() runs in the context of the calling function, but hadn't realized of how incredibly important that can be (otherwise it would be rather complex to make the arguments passing work)...

so the main part for this to work is:


var strings = [];

for(var i=0; i<arguments.length; i++)

     strings.push("arguments[" + i + "]");

            

/* as eval runs in the context of the calling function, we can use arguments with no problem */

var code = "new initialFunc(" + strings.join(",") + ")";

initialFunc._singletonInstance = eval(code);



Researching a bit more I found a trick to do this using apply, in fact, it was damn obvious, I just can create the "this" object (that in a normal invocation would be created by the new statement just before the constructor) in a previous sentence, and then pass it to the apply function as its "this".
I mean:


var obj = new Object();
initialFunc.apply(obj, arguments);
obj.constructor = initialFunc;


problem with this is that for some reason the prototype chain does not seem to work Of course it doesn't work, at the time of this article I was terribly confused about how prototype chains are implemented, the functions are not looked up in obj.constructor.prototype or something like that, but in the [[prototype]] that got assigned to obj at creation time, just after new runs and before it gets passed as this to the "constructor" function, so method invocations on the new object created this way fail...

You can find the complete source here

No comments:

Post a Comment