Real-world example of why global variables are bad

We all know that global variables are bad. Sooner or later, someone is going to pick the same name for a variable as you do, and when that happens, *poof!* your code doesn’t work anymore.

Unfortunately, not every programmer understands why globals are bad. This is a write-up of a rather spectacular bug that I was asked to look into during my glory days at InternetBroadcasting. It involved tracking down a conflict between an advertisement on the same page as a Microsoft VirtualEarth map control. Needless to say, when we contacted Microsoft to make them aware of the problem, they were less than responsive… it was easier to talk to the small ad serving company to get them to fix their part, and that was ultimately the route that the project manager decided to take to fix the problem.

What the problem was

A bug was reported wherein a Microsoft VirtualEarth map would not display on certain web pages that contained a certain advertisement.

How it happened

In summary, the problem occurred because two third-party JavaScript files were included on the same web page, and both scripts contained code which did not follow the extremely important best practice of limiting the use of global variables.

JavaScript code included from Microsoft’s VirtualEarth library defined a global variable named “g.” JavaScript code from ContextWeb sourced in for an advertisement also defined a global variable named “g.”

There can only be one global variable named “g” on any given web page. In JavaScript, the last value assigned to a variable is always the current value. Here is the order of events as they happened on the websites that experienced this problem:

  1. A script tag loads Microsoft’s VirtualEarth library, wherein a global “g” is defined.
    /* This runs in the global space, i.e. outside of any function or object */
    function g(a){
            return new Gimme.object(Gimme.query(a))
    }
    

    (as found in http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2)

  2. A script tag loads in ContextWeb ad code, wherein the global “g” is overwritten to a new value.
    /* This runs in the global space, i.e. outside of any function or object */
    var g=''; /* Here is the initial overwrite... many follow below... */
    var k='';
    var j='';
    var tw=w;
    try {
    	k=d.location;
    	if(k==top.location){
    		g=k;
    	} else {
    		while(true){
    			g=tw.document.location;
    			.............
    

    (as found in http://tag.contextweb.com/TagPublish/getjs.aspx?action=VIEWAD&cwrun=200&cwadformat=728X90&cwpid=501657&cwwidth=728&cwheight=90&cwpnet=1&cwtagid=25722)

  3. A script initializes the VirtualEarth map, which attemps to use the variable “g.” Having been redefined, the variable does not contain what VE code expects it to, and the map cannot be created.

Why it happened

A typical problem when integrating two third-party JavaScripts into the context of a web page is a global variable conflict. A global variable is one that is available to all scripts running in a particular context. In a web browser, all global variables are members of the object called “window,” and can be referenced simply by typing the name of the variable. Because JavaScript allows one to rewrite or delete variables created in this space, one can define and rely on a global variable and have that variable overwritten by another script which naively uses the same variable name.

Many of today’s most respected JavaScript developers recommend against the usage of global variables (for example, see Douglas Crockford’s take on the subject). Unfortunately, the usage of globals is rampant. The reasons for this are many, including that:

  • global variables are convenient

    It is extremely easy to use a global variable, and many online tutorials unfortunately contain examples wherein globals are used. Many first-time programmers are taught to use a procedural programming style with JavaScript and to write just enough code to get the job done (without namespaceing or context-checking).

  • JavaScript programmers are relatively inexperienced

    JavaScript has only recently emerged as a programming language for web applications; in the past, it has been used for very simple tasks on web pages. Now that it is being used to write full-blown applications, problems such as global variable conflicts become extremely serious. Lacking understanding of best practices, even programmers who are fluent in other languages can make terrible mistakes in JavaScript.

  • the language has some confusing syntax that promotes the use of global variables
    • In the global space (i.e., outside of any function or object) any variable defined with the var or function keyword is global. This is what happened in the case of VirtualEarth and ContextWeb. For example:

      function f() { ... }
      var t = 4;
      alert(typeof window.f); /* "function" */
      alert(typeof window.t); /* "number" */
      
    • Inside a function, the declaration of a variable without the keyword var makes the variable global. Programmers new to JavaScript often are not aware of this. For example:

      
      function myFunc() {
      	/* LOCAL VARIABLES */
      	var myVariable = 3;
      	var myFunction = function() { return 3; }
      
      	/* WHOOPS - GLOBAL VARIABLES */
      	myOtherVariable = 4;
      	myOtherFunction = function() { return 4; }
      }
      myFunc();
      
      alert(myVariable); /* undefined */
      alert(myOtherVariable); /* 4 */
      alert(window.myOtherVariable); /* 4 */
      
      alert(myFunction()); /* error, myFunction is not defined */
      alert(myOtherFunction()); /* 4 */
      

How it could have been avoided

  • Namespacing

    In JavaScript, the practice of namespacing is simply creating one global variable that is unlikely to create a name conflict (such as the organization name of a company in capital letters). All variables are then stored within that object, and can only be accessed through the object. For example:

    var IBSYS = {};
    IBSYS.foo = 3;
    alert(foo); /* undefined */
    alert(window.foo); /* undefined */
    alert(IBSYS.foo); /* 3 */
    

  • Scoping

    Javascript allows the creation and immediate execution of unnamed (i.e., anonymous) functions. Functions have their own local scope, and thus any local variables defined in an anonymous function will not conflict with any globals or code in any other scope. For example:

    (function() {
    	var t = 3;
    	alert(t); /* 3 */
    })();
    alert(t); /* undefined */
    

Comments

  1. Jackson T
    January 30th, 2009 | 12:15 pm

    Thanks for the refresher. I had a total facepalm moment reading through this in regards to ‘var-ing’ variables within scope of a function.

  2. morman
    January 30th, 2009 | 1:57 pm

    Thanks for drawing attention to this all-too-common problem.

  3. February 2nd, 2009 | 9:28 am

    Do you think projects like ADsafe http://adsafe.org/ and Caja http://code.google.com/p/google-caja/ could help? Have you had a chance to play with either of these?

Leave a reply