When running the following block of code, FF and Chrome output typeof(hiya) = string
while IE7/8 output typeof(hiya) = undefined
.
<html>
<body>
<script type="text/javascript">
window.hiya = 'hiya';
</script>
<script type="text/javascript">
if( false ) {
var hiya = 1;
}
document.write( "typeof(hiya) = "+ typeof(hiya) );
</script>
</body>
</html>
Each of the following makes the problem go away:
- Combining everything into a single
<script>
block.
- Removing the
if
block.
- Renaming
var hiya = 1
to var hiya2 = 1
.
- Renaming
var hiya = 1
to window.hiya = 1
.
- Renaming
var hiya = 1
to hiya = 1
.
What is happening? Is there a scoping bug in IE?
IE is dumb, it doesn't recognize that window.varName
and var varName
access the same variable in some cases.
When a new script tag is encountered, it first initializes all the variables declared with var. It doesn't run the var statement (the part that would initialize it to "hiya"). It just initializes it to undefined. It won't do that if it was previously declared with var though.
If your code was in a single script tag, this error would not happen. Also, if the first declaration of hiya was done with var, this error also wouldn't happen.
Specifically, in your second script tag, IE first looks for var statements, it finds a var var hiya = 1
; Then it says, hiya hasn't been initialized with a var statements previously (IE being dumb, other browsers recognize that window.hiya does the same thing) and initializes hiya, overwriting window.hiya before executing any code.
Possible solutions:
- Keep your code within the same script tag
- Do not initialize variables with window.hiYa
- If you don't control over one of the scripts, make sure the script that uses var comes first
Last note to clarify what JS parsers do to your code. When the JS parser sees your code, it transforms it into the following:
<html>
<body>
<script type="text/javascript">
window.hiya = 'hiya';
</script>
<script type="text/javascript">
// IE is dumb, it doesn't recognize that hiya is already
// defined as window.hiya, so it's initialized to undefined here
var hiya;
if( false ) {
hiya = 1;
}
document.write( "typeof(hiya) = "+ typeof(hiya) );
</script>
</body>
</html>
So if you put everything into one script tag, this is what the code would be (after the JS engine moved the var statements to the top), so you can see that there is no way the IE could mess it up, since your window.hiya
assignment would be after the var that was moved to the top.
<html>
<body>
<script type="text/javascript">
var hiya;
window.hiya = 'hiya';
if( false ) {
hiya = 1;
}
document.write( "typeof(hiya) = "+ typeof(hiya) );
</script>
</body>
</html>
The core issue can be seen here http://jsfiddle.net/Raynos/UxrVQ/ I have yet to find out why IE overwrites window.hiya without checking.
[Edit]
From the specification. Page 38:
For each VariableDeclaration or
VariableDeclarationNoIn in the code,
create a property of the variable
object whose name is the Identifier in
the VariableDeclaration or
VariableDeclarationNoIn, whose value
is undefined and whose attributes are
determined by the type of code. If
there is already a property of the
variable object with the name of a
declared variable, the value of the
property and its attributes are not
changed.
A possible explanation could be that in global scope IE differentiates between the window
object and the variable object
for global scope when declaring variables. Alternatively setting a property on the window
object directly might not set the same property on the variable
object. If you can find a formal JScript specification or have the source of IE lying around then we can find out exactly what the quirk is.
[/Edit]
Thanks to @TimDown & @JuanMendes pointing out that's an issue with whether writing a property to the window object is a variable declaration.
The Issue:
variable declaration gets moved to the top of the block. Even if the code is dead. In IE for some reason it will declare hiya as a local variable even though it classes with the property of the same name stored on window.
Explanation:
What's happening is that your declaring a variable called hiya. The var statement automatically gets removed to the top of the block. An if statement isn't a block, a function is. So event if the code never gets run in the block the variable still gets declared.
In firefox it'll recognise that window.hiya is a declaration of hiya.
In IE the declaration in the second script overwrites it
What it's actaully doing
In firefox:
// script block 1
var hiya; // window.hiya counts as a declaration
window.hiya = "hiya"; // set
// script block 2
if (false) hiya = 1;
document.write(...)
In IE:
// script block 1
window.hiya = "hiya";
// script block 2
var hiya; // redeclared here because window.hiya "isn't" a declaration
if (false) hiya = 1;
document.write(...)
The solution is simply namespacing. You're using the same name in two places and accessing it in two different names. Either use different names or use closures to give local scope.
What you encountered is due to:
var
being a statement
- There's no block scope in JS
- Statements get executed before the code runs
So what happens is that JavaScript will execute the var
statement before before anything else, but it will not evaluate the assignment expression, therefor hiya
will default to the value of undefined
.
As Raynos already stated IE will execute each scripts on its own, therefore the behavior described above, will results in hiya
being undefined.