While it's no mystery that writing portable code for the web is rather a painful task, professional javascript developers tend to blame (with good reasons) browsers' makers deceitful DOM implementations and usually bash their way through using some kind of leveraging library.
The language in itself (ECMAScript, pretty much) usually is considered a common and consistent basis.
Or is it?
var fake = {
"flush": "yes!!!!"
};
new (function(window){
var localVar = "yes as well";
with(window){
var otherVar = "yes, third";
var functionLiteral = function(){
alert("flush? function literal -> " + flush + " " + localVar + " " + otherVar);
};
function functionStatement(){
try{
alert("flush? function statement -> " + flush + " " + localVar + " " + otherVar);
}catch(e){
// XXX Safari and IE will have "flush" and "otherVar" undefined
}
}
var functionStatementWithAssign = function functionStatementWithAssign(){
alert("flush? function statement with assign -> " + flush + " " + localVar + " " + otherVar);
};
alert("flush? inside body -> " + flush + " " + localVar + " " + otherVar);
functionLiteral();
functionStatement();
functionStatementWithAssign();
}
})(fake);Now, no cheat, no DOM, no activeX magic, no fancy XHR stuff, no nothing but pure scope manipulation.
Depending what "your favorite browser" is, you will get an alert on the functionStatement() call, or not. Note that you'll get proper alerts with all the other calls.
So, first, which browser is right? Simple answer: none :-)
Why is this?
Let's first analyze the code
Pretty much, you have a closure that defines a private scope:
new (function(window){
})(fake)which scope is then extended by a with statement using the passed argument.
with(window){
}
That means (if you are still running) that you pretty much sandbox the inside code into a new "global" object (at least, have the feeling of).
What then? The inside code defines a function literal, dereference a function statement, and adds a function statement, then call alert, then each three of the functions.
Surprise: the function statement doesn't honor the local scope in Webkit, Opera, and IE - while it does in Gecko.
Surprise: all the other calls work "as expected".
Understanding the wtf
For quite a long time (prior to js 1.2), Ecmascript allowed functions to be defined only in top-level global code.Then ECMAScript introduced functions literals (in addition to function statements), that could be added anywhere.
Then, the restriction on functions statements was softened: function statements could appear inside other functions statements.
Still (and this is the tricky part): they are still restricted to top-level global code and top-level function code. But wait, having only that would have led to dozens of angry web developers... so... browsers makers all had an extension to their ECMAScript implementation, on an unspecified point, allowing to declare function statements anywhere (with.. yes, unpredictable results)
Typically, in our example, the function literal works as expected, the function statement assigned to a local variable as well (being actually bound to the local scope), but the simple function statement is bound to global-level code for 3 out of 4 major browsers and to the local scope for the fourth one... Technically, this is because of the enclosing "with" statement (while that would be true as well for other conditional statements with scope changes).
Conclusion
Obviously, you should be extra careful with function statements and stick to the fact that the specification only allows them to be declared in top-level code.If you actually plan on doing anything more fancy than that, use function literals or assign to variables.
Well... try this with your favorite browser: