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

Wednesday, September 20, 2006

anonymous function prototype

There are several scripts that use anonymous functions and we often use them as regulars.
Is there a way to have different or dedicated prototypes for this kind of function ?

Step 1, what is anonymous function ?
The "original" anonymous function is returned from the global Function object.

var myFunc = new Function("a", "b", "c", "return a + b + c;");
alert(myFunc(1,2,3)); // number 6

myFunc is a function with all Function prototypes or native methods and with the same constructor of a function.
Then myFunc is absolutely a function, but wich kind of function is it ? Exactly this one:

function anonymous(a, b, c) {
return a + b + c;
}

What's that ? That's a function that exists but you can't modify, get, or extend anyway because each new Function will produce a different referer for a different anonymous function.

(new Function).prototype.isAnonymous = true;
alert((new Function).isAnonymous); // undefined

You can try in a different ways creating for example a personal anonymous function, but the result will be the same ...

var anonymous = new Function;
(new Function).prototype.isAnonymous = true;
alert((new Function).isAnonymous); // undefined

// other way

function anonymous(){};
anonymous.prototype.isAnonymous = true;
alert((new Function).isAnonymous); // undefined

That's becaus, as I've just said, every anonymous instance is different from every other.
With FireFox you should view this difference using toSource Obect native method.

function anonymous(){};

var realanonymous = new Function;

alert([
anonymous.toSource(), // function anonymous() {}
realanonymous.toSource(), // (function anonymous() {})
anonymous === realanonymous // false
]);

As you can see, those parentheses are the key to understand the anonymous function.




Step 2, parentheses and virtual scope
At this point we know that an anonymous functions cannot have dedicated prototypes and aren't like regular functions too.
In some script you can see the use of parentheses to call runtime a function or to create one.
This code, for example, is an unobtrusive way to add a personal String prototype.

String.prototype.toArray = (function(base){
return base || function(){return this.split("")}
})(String.prototype.toArray);

alert("test".toArray()); // t,e,s,t

Let me explain this few lines of code.
If some script, before this one, has just defined a String.prototype.toArray or browser has a native String.toArray function, the anonymous function created using parentheses and directly called with (String.prototype.toArray) that accepts, if present, the base function, will assign to that prototype old version (base) or, if base is not defind, our prototype function (function(){return this.split("")}).
The closed anonymous function is then a special function and its really usefull to solve a lot of problems.
This is only a little example but I think you used anonymous functions every day with every scripts ;)
Since the scope inside parentheses "magically disappear", but neither for itsself nor for its internal scope, we can think that those kind of functions are exactly anonymous.

var myAnonymous = (function anonymous(){}),
realAnonymous = (new Function);

alert([
myAnonymous, // (function anonymous(){})
realAnonymous, // (function anonymous(){})
myAnonymous === realAnonymous, // false
myAnonymous.toSource() === realAnonymous.toSource()
// true !!!
]);


Step 3, how to create a dedicate prototype
JavaScript is Object Oriented and each function is an object, then why I couldn't use "special" anonymous functions as an object ?
That's why I've created a simple solution to have customizable anonymous functions, every one will be different from every other, but everyone will have our dedicate prototypes.
This is the concept function

// anonymous explicit function
function anonymous() {

// prototype to prototype,
// this function copy each a prototype (p) to other (b) function
function p2p(p,a,b) {

// using prototype for b too isn't a good solution (imho)
// because only new anonymous will has these prototypes
for(var k in a[p])b[k] = a[p][k];
return b;
};

// we need arguments and its length plus a genric array
var l = arguments.length, a = [];

// if argument is not one or its not a function
if(l !== 1 || arguments[0].constructor !== Function) {

// create the string ([arguments[N],...,arguments[0]])
while(l)a.push("arguments[".concat(--l,"]"));

// then reverse ...
a.reverse();

// ... to assign anonymous function to arguments 0
eval("arguments[0]=new Function(".concat(a.join(","),")"));
}

// return its "prototyped" version of anonymous function
return p2p("prototype", arguments.callee, arguments[0]);
};

The major difference from native new Function(arguments) is that my anonymous function accpets an anonymous function too.
Here there's a complete test to view some application.

// anonymous explicit function
function anonymous() {
function p2p(p,a,b) {
for(var k in a[p])b[k] = a[p][k];
return b;
};
var l = arguments.length, a = [];
if(l !== 1 || arguments[0].constructor !== Function) {
while(l)a.push("arguments[".concat(--l,"]"));
a.reverse();
eval("arguments[0]=new Function(".concat(a.join(","),")"));
}
return p2p("prototype", arguments.callee, arguments[0]);
};

// common Function prototype
Function.prototype.isFunction = true;

// only anonymous prototype
anonymous.prototype.isAnonymous = true;

// first test --------------------------------------
// new anonymous creation with the same sintax of new Function
test = anonymous("str", "alert(str)");

// just few checks
alert([
"" + anonymous.isAnonymous, // undefined, anonymous is a function
"" + test.isFunction, // true, test is a function
"" + test.isAnonymous // true, test is an anonymous function
]);

test("Hello World!"); // Hello World! [then test works perfectly]
// _________________________________________________



// second test --------------------------------------
// common function declaration using anonymous
test = anonymous(function(str){alert(str.toUpperCase())});

// just check it
alert([
"" + test.isFunction, // true, test is a function
"" + test.isAnonymous // true, test is an anonymous function
]);

test("Hello World!"); // HELLO WORLD! [then test works perfectly]
// _________________________________________________



// third test --------------------------------------
alert(anonymous(function(){}).isAnonymous); // true
// _________________________________________________



// final test --------------------------------------
anonymous(function(a){alert(a + arguments.callee.isAnonymous)})("Anonymous ? ");
// true
// _________________________________________________

Just a look at the last test, where is used arguments.callee instead of "this".
That's simply why the "this doesn't exists" inside the function ("this" inside a function is the window object).
That's all :)

1 comment:

Andrea Giammarchi said...

maybe some example during the post isn't well formed ...

When you set a prototype to a function, only using this one as constructor will set the prototype (I forget some "new").

Then this should be a better example:

function anonymous(){};
anonymous.prototype.isAnonymous = true;
var test = new (new Function);
alert(test.isAnonymous); // undefined

However, the problem is the same, and the solution is the same too :)