10

(creating a separate question after comments on this: Javascript redeclared global variable overrides old value)

I am creating a globally scoped variable using the square bracket notation and assigning it a value inside an external js file.

In another js file I declare a var with the same name as the one I just created above. Note I am not assigning a value. Since this is a redeclaration of the same variable the old value should not be overriden as described here: http://www.w3schools.com/js/js_variables.asp

Create 2 javascript files with the following content : Script1

//create global variable with square bracket notation
window['y'] = 'old';

Script2

//redeclaration of the same variable
var y;

if (!y) y = 'new';

alert(y); //shows New instead of Old in IE

Include these 2 files in your html file

<html>
 <head></head>
 <body>

  <script type="text/javascript" src="my.js"></script>
  <script type="text/javascript" src="my2.js"></script>

 </body>
</html>

Opening this page in Firefox and Chrome alerts 'old' which is the expected behavior. However in IE 8 the page will actually alert 'new'

Any ideas on why this happens on IE ?

Community
  • 1
  • 1
Yousuf Haider
  • 178
  • 2
  • 9
  • What happens if you put all the code inline in the HTML file? It gives the same result (old) for me in Firefox 3.5.8, Chrome 5.0.342.7, and Konqueror 4.3.5. Results for other browsers would be useful. – Matthew Flaschen Apr 14 '10 at 05:36
  • If you put all the code in one file, hoisting would occur and the problem would likely not be present. – Justin Johnson Apr 14 '10 at 05:43
  • Yes if you put all the code in one single place alert shows 'old' on all browsers – Yousuf Haider Apr 14 '10 at 05:51

3 Answers3

10

Simplified test case:

<script>
    window.foo= 1;
</script>
<script>
    var foo;
    alert(foo);
</script>

And yes, this absolutely is a bug in IE's JScript engine.

Why does it happen? Why does IE do any of the crazy things it does? Make an irritated noise, move on, try to avoid doing this...

bobince
  • 528,062
  • 107
  • 651
  • 834
  • 2
    If you've narrowed it down to “something wrong with you” and “something wrong with IE”, go with IE every time... – bobince Apr 14 '10 at 18:34
4

If you expect y to be global, you can just drop the var y line altogether in your second file.

The reasoning behind this is that since you want y to be global anyway, just treat it like its a global and already declared. JavaScript's side-effect of making variables global when declared without the var prefix plays to your favor in this situation. Tested in IE8, this works just fine.

Edit: As to why this happens, I'd chalk it up to it just being a bug in a combination of IE's handling of globals across files and declaration hoisting. Really, though, you should only be declaring any variable, but especially a global, in one place. Your problem can be avoided by following this rule of thumb.

Justin Johnson
  • 30,978
  • 7
  • 65
  • 89
  • 1
    Thanks for that suggestion. Makes sense. Though it would still be interesting to know why this doesn't behave correctly in IE. – Yousuf Haider Apr 14 '10 at 05:55
  • 1
    Surprisingly, it seems that IE may have a bug in its JavaScript implementation. – Justin Johnson Apr 14 '10 at 07:10
  • Yes, ideally we would want to declare a global in only one place. My use case was that I had multiple js files each of which was declaring a namespaced class using this pattern: Script1 : if (!a) a = {}; a.b = function() {....}; Script2 if (!a) a = {}; a.c = function() {....}; Now this works fine until the first script uses window.a or window['a'] to declare a and then IE breaks. Since multiple pages can choose which files to include each of them have to check whether a exists. – Yousuf Haider Apr 14 '10 at 20:02
  • I figured as much. This is similar to `dojo.declare`. You might want to take a look to see how they did it or maybe even just use dojo: http://api.dojotoolkit.org/jsdoc/1.3/dojo.declare – Justin Johnson Apr 15 '10 at 00:22
  • Funnily enough this bug came up because I had my own custom dojo widget as well as my own class using the same namespace prefix. For eg. a.myDojoWidget (dojo widget) followed by a.myClass (simple js class) The problem is that dojo.declare declares the class in this fashion: window['a'] = {}, and then a['myDojoWidget'] = .. I've avoided this issue (based on your suggestion) by not doing var a. – Yousuf Haider Apr 15 '10 at 01:42
1

This is happening in IE because the line of re-declaration is setting y to undefined. Then the line testing if y is not set passes and y changes to "new".

Change the second script to:

//redeclaration of the same variable
var y;

alert(y); // is undefined in IE

if (!y) y = 'new';

alert(y); //shows New instead of Old in IE
Jonathan
  • 5,953
  • 1
  • 26
  • 35
  • 1
    Right I understand that. But if you were to merge the contents of the 2 files in one single place y would no longer be undefined and the redeclaration would not set y to undefined. As a result the alert would show 'old' – Yousuf Haider Apr 14 '10 at 05:53
  • In IE all declarations are executed before other parts of the code. In the second include if you make the first line alert(window['y']) you'll see that the value is undefined. If you comment out the redeclaration line you'll see the expected 'old' appear. – Jonathan Apr 14 '10 at 06:07
  • Actually, this happens in all browsers and is called *hoisting*; however, it seems that IE is doing it wrong in this scenario and causing an overwrite. – Justin Johnson Apr 14 '10 at 07:12