Saturday, May 23, 2009

[ECMAScript 5] Do Not Remove arguments.callee !

Being subscribed in dunno how many developers mailing list, I completely forgot to follow up the arguments and arguments.callee discussion.
Accordingly to this John post, is with extreme surprise that I am discovering how critical is the gap between programming language developers and programming languages active users.

Even if I read more than once piece of SpiderMonkey or Google Chrome, I am part of programming languages users and I can tell you for sure that the decision to remove arguments.callee from the language will be a complete mess.

The Main Aim Of ECMAScript 5: Do Not Break What We Have So Far

Unfortunately, Internet Explorer will be broken, because named function persists in the scope, causing naming conflicts everywhere!


setTimeout(function f(){
alert(window.f);
}, 1000);

Above code will alert the function f in every Internet Explorer. Moreover, as you can read in one comme of John's entry, Internet Explorer has function interpretation "problems", declaring or overriding already declared function even if these are in an else case, while other browsers do not even consider that part of code if it will never be executed.

Another Attemp To Slow Down The Already Slow Browser


Reading this long thread about the discussion, some clever guy suggested a simple solution to avoid naming conflicts ... a closure

(function(){
setTimeout(function f(){
alert(window.f);
}, 1000);
})();

This simply means that our code size will drastically increase without a valid reason and number of created scopes and functions will be twice as was before (already too big). They think it will not slow down performances but they are forgetting old browsers that will be still used when all this wonderful shiny JavaScript will be released: Internet Explorer 7, and Internet Explorer 8 (assuming Internet Explorer 6 does not exist anymore ... would be nice to start to fight against the version 7 and then 8 ...).

Introspection and BackTrace? They Decided It Is A Bad Thing!


Without arguments.callee we are loosing arguments.callee.caller as well, often the only way we have to truly debug our code. At the same time the dynamic nature of JavaScript where "you can inject whatever wherever" will loose a big portion of its malleability.

Everything For Performances Reason ???


Apparently what this "new" language would like to get rid off is arguments variable, probably the most used variable ever, the inspiration of every ArrayLike object or library. This would be for performances, where we need as first step to call 2 functions rather than one for each runtime function assignment (setTimeout example and every assigned inline lambda) and where everybody will try to overload the language itself providing other methods to emulate the good old stuff!

Let's Start To Think About Re-Implementation Of Callee

As soon as I realized the news, I decided to think how to put again, and against performances, the callee behavior.

function F(original){
return function(){
var f = window.callee,
r = (callee = original).apply(this, arguments)
;
window.callee = f;
return r;
};
};

Above snippet execution time will be almost the same of a closure with mainly two advantages

  • less code to write

  • more performances when needed


Here an example:

var i = 0;
setTimeout(F(function(){
if(i++ === 1)
alert("OK");
else
setTimeout(callee /* please read more */, 1000);
}), 1000);

Since JavaScript is single thread, the instant that lambda will be called the global variable callee will refer to the lambda itself. Cool, but it is not enough.
To be sure that we do not loose the callee because of some other function assignment, we should trap again the callee inside that execution:

var i = 0;
setTimeout(F(function(){
if(i++ === 1)
alert("OK");
else
setTimeout(F(callee), 1000);
}), 1000);

There we are, a quick and dirty solution to the problem ... but, in this case for each timeout we are creating another function within its scope. This does not happen if we use the closure strategy so, again, we are trapped and we need to add more layers in the middle to increase responsiveness for something designed to improve performances ... does it make any sense?

function F(original){
function callee(){
// save the current window.callee
var f = window.callee, r;
// assign this callee function
window.callee = callee;
// retrieve the result calling original
// in the original scope, callee will be
// this whole function
r = original.apply(this, arguments);
// assign back the window callee
window.callee = f;
// return the result
return r;
};
// callee.callee will be the lambda
// rather than its wrap (callee itself)
callee.callee = original;
// here we are
return callee;
};

With above "monster" we have a slightly slower function that will always reassign the correct callee whatever will happen in the original function scope. This meanse simply that we can now use directly callee:

var i = 0;
setTimeout(F(function(){
if(i++ === 1)
alert("OK");
else
setTimeout(callee, 1000);
// callee.callee for direct access to the lambda itself
}), 1000);


I Bloody Hate This Idea

... but if they do not change their mind, it will be one of few other possibilities we have to avoid naming pollution everywhere causing conflicts nightmare with every Internet Explorer version less than 9.

Have fun with ECMAScript 5 and its brilliant strict mode.

No comments:

Post a Comment