My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Tuesday, July 27, 2010

Constructorification

... he he, I know the title could not be worst, but after my last post about Arrayfication I have thought: "... hey, the Thing.ify(object) could be more than handy in many occasions such mixins and duck typing ...".

So, let me introduce the Function.prototype method that nobody will ever use:

Function.prototype.ify = function (o) {
for (var
self = this,
p = self.prototype,
// find a fucking way to implement this in ES3
// ... uh wait, there's no way to implement
// this in ES3 ...
// https://bugzilla.mozilla.org/show_bug.cgi?id=518663
m = Object.getOwnPropertyNames(p),
i = m.length, n; i--;
) {
// methods only
m[i] = typeof p[n = m[i]] == "function" ?
"o." + n + "=p." + n + ";" : ""
;
}
m.push("return o");
return (self.ify = Function("p",
"return function " +
(self.name || "anonymous") + "fy(o){" +
m.join("") +
"}"
)(p))(o);
};

Since the function requires a Object.getOwnPropertyNames call for its prototype in order to be able to inject it's properties and methods in whatever object, the ify method is lazily assigned.
This code makes "Function.ification" easy so that precedent post could be summarized via:

Array.ify(
document.getElementsByTagName("*")
).forEach(function (node) {
// do stuff with node
});

and be performed at light speed every other time while it won't cost at all until it's performed on whatever constructor the first time only.

Well, at least the technique may result interesting, unfortunately without ES5 it's not possible to reproduce the same behavior without knowing in advance all possible keys part of the native prototype (it's possible for user defined ones tho)

5 comments:

Shahen said...

But why? Why don't you just use array generics? I mean, if you want to iterate you can write Array.forEach(document.getElementsByTagName('*'), function(){...})

I've seen this many times - people are trying to convert a collection into array just for iterating or filtering or mapping. But in 98% of cases we can do it without "arrayifying"

Andrea Giammarchi said...

we can do without arrayifing 100% of the time, the point is a bit different and some browser has forEach in the proto, but no public static Array.forEach

Shahen said...

If browser yet doesn't support Array.forEach it is a good reason to implement it.

>the point is a bit different
well, then, what is the point? I'm not arguing - I'm just trying to find out your position.

Andrea Giammarchi said...

The point is that we don't necessary need to inherit Array.prototype, impossible without loosing current __proto__ for natives.
We can always attach directly Array.prototype methods if the object has not been frozen, so this is a proposed solution to do it once and in the fastest possible way.
Every constructor, native or not, could be used to duck type any kind of object then and:
1. performances will be better for each call, no lookup for the proto, simply access to object properties
2. the code is lazy evaluated and compact, so we have a huge new possibility without compromising code size
This was about "Constructorification", the Arrayification is only one part of the story :-)

Shahen said...

Oh, I see. Thank you for clarification.