Friday, November 30th, 2007
Bug: Object Killing in IE7
Jon Sykes thinks he has found a bizarre bug in IE7. Since Microsoft has 34 QA people per developer, surely not! ;)
He told us:
There is a bug in IE7 where by a line of code inside a conditional statement that NEVER runs, can cause an object that is set with a fairly standard object declaration to be whipped. Even weirder is that it will have whipped the code even if you put a debug alert of the object before the code that does the whipping. Confused? I know I am.
Thankfully it does look fairly simple to work _around_ and avoid. But it's probably a debug nightmare, and it's a bug I couldn't find referenced anywhere, so I figured it was worth sharing.
A simple test case is:
-
-
<script type="text/javascript">
-
this[‘test’] = {};
-
alert(this[‘test’]);
-
// will spit out [Object object]
-
</script>
-
<script type="text/javascript">
-
alert(this[‘test’]);
-
// will spit out Undefined in IE7
-
-
// this next chunk of code should never run.
-
if (true == false){
-
alert("This never fires");
-
// THIS SHOULD NOT IMPACT ON ANYTHING !!!!
-
var test; // take this line out and it works fine
-
alert("This never fires");
-
}
-
</script>
-












This… has to be the work of some asshole dev who got sick of hearing about how terrible IE is. And I’m sure QA wouldn’t catch it because who in the hell would ever test for that?
The smart quotes are ruining the code.
That comment about “should have no effect” isn’t exactly true. Remember that JavaScript doesn’t have block scope, it has function scope. So that var declaration takes effect at the start of the function that it’s running in. This means that “test” is a variable with the value
undefinedin that scope. Now to me it looks like IE7 is treating each script tag as it’s own function. I have no idea how correct or incorrect this is.It’s also worth pointing out that jslint would have warned you about this behaviour.
“So that var declaration takes effect at the start of the function that it’s running in.”
What? You mean the JS engine evaluates code within an if statement that evaluates false? And that’s “correct” behavior? Scope doesn’t enter into it, code that is in an if statement that evaluates false should never run.
The JavaScript engine does not evaluate the the code. It just treats all variables, which are declared using “var” as local variable in the function they have been declared in. This happens while parsing the function and is independent of the fact whether the code will actually be executed.
One exception are variables, which are declared in the global scope (outside of any function). They are always global even if they are declared using “var”.
The strange thing about the example above, as happygiraffe pointed out, is just that IE seems to treat each script tag as it’s own function.
In JavaScript, we have two scopes: global and function. Let’s take the EcmaScript specifications, which could demonstrate that your code is buggy instead.
From 12.2 - Variable statement:
In your case, “this” is “window”, the global scope. So, “var test” is the same as window['test'], or even this['test'].
Then, from the specs again:
Opps… it’s saying that all variables defined in the scope are created at the start of the code execution, not at the declaration point.
Now, right after it:
Well… it sounds like IE’s behavior reflects the specs perfectly.
At first look, it stills sounds bizarre, but well… probably the bug is the script code instead.
By that logic, the first alert should also spit out undefined.
@Trevor: that’s is not exactly true. It is also known that each block is executed separately. The second runs like a new scope execution, which inherits all properties defined in the global scope from the first block.
Weee :) I think there is no hope to have appropriate browser from Microsoft.
FredCK, your quotes from the spec prove that the “var” statement in the second script block rightly defines the variable (in the global context) although it is never reached. That is alright.
Now, the code in both script blocks has the global object as its execution context, right? The first block is parsed and executed, then the second block is parsed and executed, right?
In the first block, “test” is created. Now the second block executes. The “var” declaration should not have any effect (that is: not create and declare test as undefined), as “test” is already created.
FredCK:
If that’s true, wouldn’t that be a bug? As you said, “this” is “window”, the global scope. Is there a global scope for each script?
@Martin: I think we found the buggy part of IE.
From EcmaScript specs 10.1.3 - Variable Instantiation:
Yes, you caught it!
@Trevis: I suppose you are running the code in a page in the browser. So yes, “this == window == global scope”, and this global scope is certainly shared by all scripts running in that window.
I feel like this is something I should get why we’d need to do this…. but I don’t. I hold similar regards to comment #1
- “… who in the hell would ever test for that?”
@Dustin: you are right… probably just bad code would face this problem. But you know… it’s always nice to understand why things happen… you always learn something from it.
Maybe next time I’ll spend my time with the Big Bang instead :) or even better… coding!
I saw some code …(In visual Basic) with
if 1==2..
so it wouldn’t surprise me to see people write code like that..
@Rodrigo
You probably saw that code because there is no block comments in vb so an if 1==2 is an easy way to remove a block of code from execution.
Justin Are you site smart?
The situation came up because we’re adding Dojo to a legacy system.
The legacy system had script blocks that did an object check before creating the object:
if(typeof objectName != undefined){
objectName = {};
}
Throw in a Dojo provide before that and suddenly you’ve got vanishing objects all through your system.
I’m still confused why doing:
var test = {};
instead of
this['test'] = {};
Solves the problem. That seems to fly in the face of the ascertain that what it’s doing is correct. That said the quoted stuff above is very interesting, and opens a whole new light on it.
New test, one to remove some of the clutter.
No If Statement
Another interesting one is checking to see if the browser thinks the two variables exist:
Check this['test'] and test exist
var test = {}; probably works because the script engine now properly (internally) marks test as existent in the global scope, whereas this[‘test‘] = {} also creates the variable in the global scope but does not mark it properly. Or something like that. It is the inner working of the script engine that contains the bug, not your code.
Just to add to the discussion, each script block must be parsed and executed separately, and they cannot all be parsed together and then executed together, because the execution of one script block may affect whether or not the next script block runs. For example, imagine a scenario where the first script block uses document.write() to write out a comment (
… and the comment causes the remaining script blocks to be commented out and thus invalid.
Who the hell is this sitesmart person that keeps posting soft spam in the comments?