VK wrote:
I was getting this effect N times but each time I was in rush
to just make it work, and later I coudn't recall anymore what
was the original state I was working around.
And you wonder why nobody takes you seriously when you label yourself a
"programmer"?
This time I nailed the bastard so posting
it before I forgot again...
Well, there is no hope that you could analyse this for yourself.
By taking this minimum code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Demo</title>
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1">
"Minimum code" does not need a META element.
<script type="text/javascript">
// 1)
(function refByCallee() {
arguments.callee.foobar = 'foobar';
})();
// 2)
(function refByName() {
refByName.foobar = 'foobar';
})();
if (typeof refByCallee != 'undefined') {
alert(typeof refByCallee.foobar); // undefined
alert(typeof refByName.foobar); // string
}
</script>
</head>
<body>
<h1>Demo</h1>
</body>
</html>
There are two issues here: the one is well-known and
another I what I want to ask about.
1) The known issue is the difference of how JavaScript
and JScript are treating function declarations within
expression.
There is no sense in "function declarations within expression". Programs
and function bodies are made up of FunctionDeclarations and Statements
(two mutually exclusive constructs), Expressions are components of
Statements. The code above only features FunctionExpressions with
optional Identifiers.
In JScript it still leads to a function reference added
to the global namespace,
IE erroneously adds named properties referring to function objects to
the Variable object for the execution context in which the
FunctionExpressions with optional Identifiers appears. That is only the
global object in the global execution context.
just as without any parenthesis.
Without the parenthesise the constructs are unambiguously
FunctionDeclaration (to every ECMAScript engine).
This way on IE after executing 1) and 2) we have two
new global named functions while on other engines not.
On IE you actually have two named properties of the Variable object
referring to function objects _before_ the execution of 1) and 2). And
this fact is the biggest clue as to what IE is really doing when it
misinterprets this code.
2) What I'm not sure is how to explain that IE obviously
makes difference here between arguments.callee and literal
name.
You mean a difference between the object referred to by -
arguments.callee - and the object referred to by the Identifier.
While literal name acts as expected - see 2)
It only acts as expected if you appreciate the bug in IE. The
expectation derived from the language specification is that the
Identifier used outside of the function bodies should not resolve into
references to function objects, and inside the function bodies they
should refer to the same object as - arguments.callee -. It is the
difference between the formal specification that Microsoft assert
JScript follows and the behaviour of JScript that allows this to be
labelled a bug.
- arguments.callee points to some other object instance
disappearing right after the execution.
The - arguments.callee - reference should refer to the function object
that is being executed, and it does. As that function object results
form the inline execution of a FunctionExpression and no reference to
that function object is assigned to any property of any other object in
the system you should expect that object to 'disappear' following its
execution.
Apart from failing to test for the existence of the properties of the
Variable object prior to the execution of the FunctionExpressions (and
so not noticing that they do exist, and refer to function objects, at
that point), you have not verified that, for example, - refByCallee -
and - arguemnts.callee - refer to the same object. The tests is simple:-
alert('(callee === refByCallee) '+(arguments.callee === refByCallee));
- and with:-
(function fn() {
alert('(callee === fn) '+(arguments.callee === fn));
arguments.callee.foobar = 'foobar';
})();
- the result is false. The object referred to by the Identifier - fn -
is not the same object as referred to by - arguments.callee - inside the
executing function object.
Meanwhile, with:-
fn();
(function fn() {
alert('(callee === fn) '+(arguments.callee === fn));
arguments.callee.foobar = 'foobar';
})();
fn();
- the alerts are true, false and true (in that order). Prior to the
evaluation of the FunctionExpression an - fn - function exists, can be
called and is the object referred to by - arguments.callee - when it is
executing. When the FunctionExpression is executed - argument.callee -
does not refer to the - fn - function, and after the execution of the
FunctionExpression a second call to the - fn - function again results in
the execution of a function object that's - arguments.callee - refers to
the - fn - function.
The explanation for this is simply that there are two function objects
involved; a function object that is referred to by the property of the
Variable object and comes into existence prior to the execution of any
code for the execution context, and a second function object that comes
into existence as a result of the evaluation of the FunctionExpression.
The function object coming into existence with the evaluation of the
FunctionExpression is what is supposed to happen (the handling of the
optional Identifier is still wrong on IE, but then optional Identifiers
are not really expected to be used with FunctionExpressions). So it is
the creation of the first function object that represents the
manifestation of the bug in IE. The fact that these functions, and
corresponding properties of the Variable object, come into existence
prior to the execution of any code for the execution context suggests
that the fault is in the variable instantiation stage of execution.
Specifically, that at the point of scanning for function declarations IE
is using criteria for identifying Function Declarations that are
considerably more crude than the formal syntax rules for a
FunctionDeclaration. That is, it is looking for the - function keyword
followed by an Identifier followed by a matching set of braces and
taking any occurrences of that, regardless of context, as a Function
Declaration. And so finding, and acting upon, considerably more
FunctionDeclarations than exist in the actual code.
This probably represents an attempt at an optimisation for speed. It has
been observed that IE is the fastest browser at parsing javascript
source (by the fact that its - eval - function and - Function -
constructor out perform all other environments) and being a bit lax on
the syntax rules may be key to this.
The consequences of this bug are largely insignificant as nobody should
be writing code with the expectation that the optional Identifier in a
FunctionExpression should be available outside of the resulting
function, and so not be attempting to refer to that function in that
way. Your own code, for example, is stupidly designed. If you wanted to
refer to named global functions (to use them as a vehicle for passing
data about) then it would be trivial (and more obvious) to have the
function objects created globally with FunctionDeclarations. If stupidly
obtuse code is written then it likely will fall over implementation
bugs.
Richard.