Very good question. :-)
Difference:
The difference between this and your detachEvent situation is that you don’t care here that the reference to the function inside and outside the “function” is the same, just so that the code is the same. In a detachEvent situation, detachEvent is important that you see the same functional link inside and outside the “function”, because it works detachEvent , separating the specific function that you give it.
Two functions?
Yes. The CMS indicated that IE (JScript) creates two functions when it sees an expression with a named function, similar to the one that is contained in your code. (We will return to this.) It is interesting that you call both of them. Yes, indeed. :-) The initial call calls the function returned by the expression, and then all calls using the name call another.
Modifying your code a little can make it a little clearer:
var testaroo = 0; var f = function executeOnLoad() { if (testaroo++ < 5) { setTimeout(executeOnLoad, 25); return; } alert(testaroo);
f(); in the end, it calls a function that was returned by the function expression, but it is interesting that this function is called only once. At another time, when it is called using the executeOnLoad link, it calls another function called. But since two functions are closed according to the same data (including the testaroo variable), and they have the same code, the effect is very similar that there is only one function. We can demonstrate that there are two, especially since the CMS has done:
var testaroo = 0; var f = function executeOnLoad() { if (testaroo++ < 5) { setTimeout(executeOnLoad, 0); return; } alert(testaroo);
This is not just some kind of artifact of names, there are also two functions created by JScript.
What's happening?
Basically, people implementing JScript apparently decided to process named function expressions as function declarations and function expressions by creating two function objects in the process (one from the "declaration", one from the "expression") and almost certainly doing it at different times. This is completely wrong, but that is what they did. Surprisingly, even the new JScript in IE8 continues this behavior.
This is why your code sees (and uses) two different functions. This is also the reason for the name "leak", which was mentioned in the CMS. Switching to a slightly modified copy of his example:
function outer() { var myFunc = function inner() {}; alert(typeof inner); // "undefined" on most browsers, "function" on IE if (typeof inner !== "undefined") { // avoid TypeError on other browsers // IE actually creates two function objects: Two proofs: alert(inner === myFunc); // false! inner.foo = "foo"; alert(inner.foo); // "foo" alert(myFunc.foo); // undefined } }
As he mentioned, inner is defined in IE (JScript), but not in other browsers. Why not? For the casual observer, in addition to two functions, the JScript behavior regarding the function name seems to be correct. In the end, only functions enter a new area in Javascript, right? And the inner function is clearly defined in outer . But the specification really tried to say “no” that this symbol is not defined in outer (even in order to delve into the details of how implementations avoid it without violating other rules). It is disclosed in section 13 (as in specifications 3a and 5th release). Here's the relevant high-level quote:
The identifier in a Function expression can be referenced internally by FunctionExpression FunctionBody to allow a function to call itself recursively. However, unlike FunctionDeclaration, the identifier in the Function expression cannot reference and does not affect the scope of Expression.
Why did they go to this trouble? I don’t know, but I suspect that this is due to the fact that function declarations are evaluated before any instruction code (step-by-step code) is executed, while function expressions - like all expressions - are evaluated as part of the operator code when they are achieved in the control flow. Consider:
function foo() { bar(); function bar() { alert("Hi!"); } }
When a control flow enters the foo function, one of the first things that happens is that the bar function is created and bound to the bar symbol; only then will the interpreter start processing the statements in the foo tag. This is why the bar call at the top works.
But here:
function foo() { var f; f = function() { alert("Hi!"); }; f(); }
The expression of the function is evaluated upon achievement (well, perhaps we cannot be sure that some implementations do not do this before). One of the good reasons why an expression is not previously evaluated (or should not be):
function foo() { var f; if (some_condition) { f = function() { alert("Hi (1)!"); }; } else { f = function() { alert("Hi! (2)"); }; } f(); }
... doing it earlier leads to ambiguity and / or wasted effort. This leads to the question of what should happen here:
function foo() { var f; bar(); if (some_condition) { f = function bar() { alert("Hi (1)!"); }; } else { f = function bar() { alert("Hi! (2)"); }; } f(); }
Which bar is called at the beginning? The way the specification authors decided to address this situation was to say that bar is not defined at all in foo , therefore, it completely steps over the problem. (This is not the only way they could solve it, but it seems they decided to do it.)
How does IE (JScript) work? bar , called at the beginning, warns "Hello (2)!". This, combined with the fact that we know that two function objects are created based on our other tests, is the clearest sign that JScript processes are called functional expressions as function expressions and because exactly what is supposed here:
function outer() { bar(); function bar() { alert("Hi (1)!"); } function bar() { alert("Hi (2)!"); } }
We have two function declarations with the same name. Syntax error? You would think so, but it is not. The specification explicitly allows this, and says that the second declaration in the source code wins. From Section 10.1.3 of the 3rd Edition Specification:
For each FunctionDeclaration in the code, in the original textual order, create a property of the variable object whose name is the Identifier in FunctionDeclaration ... If the variable object already has a property with this name, replace its value and attributes ...
("Variable object" is how characters are resolved, and the whole "theme"). This is also unambiguous in the 5th edition (section 10.5), but, um, is much less cited.
So is it just IE, then?
Just to be clear, IE is not the only browser that (or had) unusual NFE handling, although they get pretty lonely (for example, a pretty big Safari problem has been fixed). It's just that JScript has a very big quirk in this regard. But come to this, I think that in fact this is the only current major implementation with any really big problem - to be interested in learning about others, if anyone knows.
Where are we standing
With all of the above, at the moment I stay away from NFE because I (like most people) must support JScript. In the end, it's enough to just use a function declaration, and then refer to it later (or even earlier) with a variable:
function foo() { } var f = foo;
... and which works reliably in browsers, avoiding issues like your detachEvent problem. Other intelligent people solve the problem differently , simply accepting that two functions will be created and try to minimize the impact, but I do not like this answer at all because of what happened to you with detachEvent .