Daily Archives: 9 May 2011

that = this

In my previous post, I demonstrated how it is easy to blow away global variables when scope in Javascript is misunderstood by the programmer. In this post, I have an even better example, and I also illustrate a way to prevent the global variable from being erroneously blown away.

Firstly, consider the following block of code:

<!DOCTYPE HTML PUBLIC "-/   /W3C//DTD HTML 4.0 Transitional//EN">
<html>
	<head>
		<title></title>

        <script language="javascript">

            // "person" written as a plain object
            var person = {
                showName: function () { return this.name; },
                setName: function (newName) { this.name = newName; }
            };

            var room = {
                capacity: 10,
                exits: 2,
                count: 0,
                person: 'latest person',
                addPerson: function (name) {
                    this.count += 1;
                    this.person = name;
                    var blowAwayGlobalPerson = function (name) {
                        this.person = name + ' name has been blown away';
                    }(name);
                }
            };

            var roomWithClosure = {
                capacity: 10,
                exits: 2,
                count: 0,
                person: 'latest person',
                addPerson: function (name) {
                    that = this;
                    this.count += 1;
                    this.person = name;
                    var blowAwayGlobalPerson = function (name) {
                        that.person = name + ' name has not blown away the Global variable Dave';
                    }(name);
                },
                showRoomPersonName: function () { return this.person; }

            };
                
        </script>

	</head>
	<body onload="">
        <input id="PersonObjectButton" type="submit" value="Click me First to See Person showName Method Called" 
               onclick="person.setName('Dave');alert(person.showName());" /> <br />
        <input id="InnerFunctionButton" type="submit" value="Click me to See Global Person Blown Away" 
               onclick="room.addPerson('Alan');alert(person);" /><br />
        <input id="InnerFunctionWithClosureButton" type="submit" value="Click me to See Closure Make Private Variable" 
               onclick="roomWithClosure.addPerson('Alan');alert(roomWithClosure.showRoomPersonName());" />
	</body>
</html>

Now, load that into a browser and follow this sequence of steps:

  1. click the top button first ("Click me First to See Person showName Method Called");
  2. click the next button down ("Click me to See Global Person Blown Away"); and
  3. click the top button again ("Click me First to See Person showName Method Called"). Don’t worry about the third button yet.

The reason you get an error when you go to click the top button again is that the second button’s execution blew away the global variable person and replaced it with a string. How did this happen? You may be thinking that the addPerson function is a method, and therefore, the person variable which it manipulates is local and confined to that’s method’s scope.

However, the addPerson method contains a inner function called blowAwayGlobalPerson. That is a function, and not a method. So, any reference to person in that function will be a reference to the Global person variable, not the person variable which has local scope in the addPerson  method. This is considered by many Javascript luminaries (such as Douglas Crockford) to be an error in the language itself. This is very possible, as language designers do make mistakes. For example, the original designer of Javascript (Livescript, at the time) has admitted that he made a low-level error when he was working with nulls, such that a null in Javascript has a type of object. I’m glad he cleared that up. That really threw me when I first encountered it (typeof(null)). Null should never be an object; not in any object oriented language anyway.

Anyhow, we can fix the scenario outlined above by using a closure. Check out the variable roomWithClosure in the markup above. By assigning this to that, we are giving the person variable, which has scope in the addPerson method, it’s own scope in roomWithClosure. So references to that in the inner roomWithClosure function are pointing to the same memory reference of the person variable in the outer addPerson function.

To demonstrate, follow this sequence of steps:

  1. completely re-load the page so that it is fresh;
  2. click the top button first ("Click me First to See Person showName Method Called");
  3. click the last button ("Click me First to See Person showName Method Called");
  4. click the top button again ("Click me First to See Person showName Method Called")

When you click the top button a second time, you will see that the Global variable is still alive and well. The blowAwayGlobalPerson function in roomWithClosure has “closed” over the person variable and ShowRoomPersonName displays it, as modified by blowAwayGlobalPerson (which is self-invoked when the roomWithClosure object is 1st created i.e. it is executed when the definition is parsed by the browser / client).

Awesome!

Update: I revisited this recently and would like to record a few more observations about the script above. In the object room, the variable blowAwayGlobalPerson is always going to be null. It is being assigned the result of a self-invoking function. And that function does not return anything. So, as per the rules of Javascript, the undefined value is given to blowAwayGlobalPerson. I also think it would be better style to include the var keyword on the line where this is assigned to that i.e. var that = this;