Why does IE nuke window.ABC variables?
Asked Answered
N

3

25

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?

Nenitanenney answered 5/1, 2011 at 17:0 Comment(4)
How about not writing such tricky things in the first place? ;)Magically
Hah. The real-world problem I was having was much more complex, where there was a conditional that would define something if another library hadn't loaded. It's something I can work around, but I'd really like to understand why this happens.Nenitanenney
@delnan: Snarky, huh? I thought it was pretty obvious that this was not real code, it's just a reduction that shows the problem. Like all OPs should do before posting a question.Francophile
I ran into the same thing with Ext.ns which creates namespaces from strings using window.<name>. Somewhere else, in some generated templating code from google closure, it was also trying to create into the same namespace (cci.templates) and it was overwriting the original cci namespace object because it used a var at the global scope to generate the namespace objects. At first I was baffled since it would test if (!window.cci) var cci = {}; Took me a while to get to the bottom of it. Glad you understand it now too!Francophile
F
27

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>
Francophile answered 5/1, 2011 at 18:24 Comment(3)
So exposing a variable through window.foo then declaring var foo in the global scope in a different execution context will cause IE to glitch. JScript is such an "unique" thing. Even though this is a rather rare corner case, it's a good thing to keep in mind.Whimsy
@FabrícioMatté It's not that much of a corner case. If you're using multiple frameworks and they both create namespaces (one using var and one using window[property], you'll get the error. I got it by using Ext-JS and Google Closure Templates together.Francophile
Same thing happens in IE if you call in a script in one tag, and use a variable from it in another tag. Like so: <script src="myscript.js"></script><script> method.text = "sample"; </script>Twannatwattle
U
10

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.

Urbannal answered 5/1, 2011 at 17:24 Comment(13)
Yes, I declare a variable called hiya within window (which makes it accessible as just hiya) and when I add an if block that contains a local var hiya = .. statement it will cause window.hiya to be undefined (even though the if block was never executed).Nenitanenney
The line of code doesn't get executed. It's still a gets parsed and the variable declaration gets handled at compile time rather then run time.Urbannal
This answer is not correct. If you run your code in IE, you'll see that hiya is set to "hiya". The key to this problem is that there are two separate script tags. See my answer.Francophile
@Urbannal I thought I did. I ran your code and it behaved as expected in IE. the var statement does not set hiya to undefined because, like you said, it is moved to the top of the script. Like I said, the wrong behavior doesn't occur if they are not in separate scripts. Your example shows a single script. See my answer for details.Francophile
@JuanMendes the examples were meant to show in what order your supposed to think about it. If you run the examples verbatim then in IE it will still give hiya the correct value. Examples was a bad choice of wording.Urbannal
Since your answer was already marked as the correct one, maybe you could improve your examples? Maybe showing the examples I have on my answer? Then I could just delete my answer. Also, I don't know what you mean that the solution is namespacing.Francophile
This answer isn't correct: each <script> element is treated as a separate Program in all browsers, including IE. This is trivially easy to prove: look at jsfiddle.net/timdown/JdsZX/2. The issue is more subtle, though I haven't yet got to the bottom of it.Payola
@Tim, I had already mentioned that, a few comments above! If you look at my answer (that now was marked as correct), you should be able to grasp the problem.Francophile
@Juan: Your answer doesn't completely explain what's going on either: there's vagueness around the crucial bit, which is precisely why IE sets hiya to be undefined in the second script, which is contrary to the ECMAScript spec if window is assumed to be the global object. The spec says that during variable instantiation, it should be using the global object as the Variable Object, observe that the Variable Object already has a property called "hiya" and then do nothing. The assignment part is not executed. Also, it doesn't look to me like you made the point I made in your comments.Payola
@TimDown you are correct. We can identify what the issue but what causes IE to create the issue under the hood is on my part speculation.Urbannal
@TimDown @Raynos: To me this is clearly a bug in IE when it runs a new script tag. It should recognize that window.hiya already exists, but it doesn't, I'm not sure which part is unclear.Francophile
@Juan: I've explained precisely what's unclear: why does IE (incorrectly, if window is the global object) set hiya to be undefined in the second script? A further obvious question is what are the exact circumstances that trigger this issue, and where else could this problem therefore arise? I don't know the answer to these, and it seems you don't either.Payola
@Tim The answer is, it's IE's bug! The exact circumstance? When you define a variable in the first script tag using window.varName and then you have another script tag that does var varName in a conditional that never gets executed. We're definitely having communication issues here, I don't see which part of my explanation is unclear. Can you comment on my answer, which part still doesn't make full sense?Francophile
M
3

What you encountered is due to:

  1. var being a statement
  2. There's no block scope in JS
  3. 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.

Mosely answered 5/1, 2011 at 17:35 Comment(1)
Raynos's answer is incorrect. All browsers execute each <script> element as a separate Program, not just IE. The problem is more subtle than you're giving it credit for.Payola

© 2022 - 2024 — McMap. All rights reserved.