Counters example

Goal

The goal is to create a web page which contains several independent counters. Each time a counter is clicked, it is incremented. Here’s you can try out a working example of what’s wanted.

Implementation

Namespace

We define and execute an anonymous function in order to create a private namespace. Just like any other function, variables declared and used in the function are not accessible from outside, unless we explicitly provide access. Returning a value is one way to do this.

(function()
{

This use of function to provide a namespace is similar in some ways to the module concept in Python. Some people call this use of function in JavaScript the module pattern.

Create

From one point of view, the create function should be, but is not, a built-in function in JavaScript. Instead we have to create it ourselves out of the new function, which is but should not be part of JavaScript.

That point of view is that JavaScript’s prototype inheritance is best understood for what it is, rather than presented as if new in JavaScript is similar to Java’s new. That point of view also say’s that code should be written to use create when required, but that use of new is considered harmful (except to define create, as below).

    var _create_fn = function(){};
    var create = function(parent){

        _create_fn.prototype = parent;
        var instance = new _create_fn();
        return instance;
    };

The create function creates a new object whose parent in the inheritance tree is the parent argument. What could be simpler than that?

The new operator has semantics such that the above code provides an implementation of create. However, we don’t for now need the somewhat complex semantics of new.

Global object

JavaScript has a global object, whereas Python does not. (Python’s main module is similar but in many important ways different.) JavaScript’s global object can be the cause of many obscure and hard-to-diagnose problems, and so it’s generally best to take care when using it.

Therefore, it is best to make explicit any use of the global object, and a good way to do this is to introduce a variable, called global of course, whose value is the global object. That way, when you access the global object or its properties, you can see that’s what you’re doing.

    var global = (function(){return this;})();

This line of code executes an anonymous function which returns the value of this during the execution of the anonymous function. Due to the semantics of JavaScript and the way the function is called, in the anonymous function this is the global object. Thus, the anonymous function returns the global object, which we store in the global variable (which confusingly is local to the function).

In some situations, such as above, the global object is available as the value of this, but sometimes this refers to something else. So using this to refer to the global object is not a good idea.

In browsers the global object has an attribute called window whose value is the global object. It is as if we had written

global.window = global;

It is better to write window rather than this, but writing global for the global object is the best. (Command line JavaScript interpreters don’t start with window as the global object, and in the browser window has many special properties.)

Counter class

Every class needs a prototype object. We’ll call it counter. Don’t confuse the counter prototype with Counter instance, which might also be called counter. Fortunately, in well organised code the prototype object of a class is in one namespace/module, and instances are in different namespaces.

Here’s the implementation of Counter. It relies on a function __init__, similar to Python’s __init__, which we haven’t defined yet.

    var counter = {};           // Prototype object for Counter.

    var Counter = function(){

        var instance = create(counter);
        instance.__init__.apply(instance, arguments);
        return instance;
    };

A word about the use of apply. In Python we would write something a bit like

def Counter(*args, **kwargs):

     instance = object.__new__(counter)
     instance.__init__(*args, **kwargs)
     return instance

In JavaScript we don’t have keyword arguments, and instead of args we have a keyword arguments which has special properties. These properties, together with those of apply, cause the JavaScript code above to have the same general effect as the Python code.

The gist of the above code is that

var my_counter = Counter(arg1, ...)

causes my_counter to be an object, whose parent is the counter prototype, and which has been initialised by the __init__ function.

Counter properties

The __init__ method is called, of course, when a newly created child of the counter prototype needs to be initialised. Each counter has a name, the thing being counted, and its count.

    counter.__init__ = function(name){

        this.name = name;
        this.count = 0;
    };

When a counter is clicked, its count is increased by one. We’ll come to the display of counters next.

    counter.onclick = function(event){

        this.count ++;
    };

Often, the most efficient way of changing the content of a DOM node is to use its innerHTML property. This method works well with delegation, as with delegation we don’t have to add (or remove) handlers from nodes created (or destroyed) in this way.

This implementation is very simple. In a production system name would be escaped. The argument parent_id is provided in case the html method wants to create IDs for its subnodes. This would happen, for example, if the returned HTML was for a slideshow with its controls.

    counter.html = function(parent_id){

        return this.name + ' ' + this.count;
    };

Onload

This code is specific to a particular page or perhaps group of pages. It creates Counter instances (sensible, as we want to count) and sets up links between the DOM and the JavaScript.

We can’t change the DOM until the nodes we wish to change have been constructed. In browsers the global object has an onload event that can be used to solve this timing problem.

Here, when the page loads we create an array of counters.

    global.onload = function(){

        var models = [
            Counter('apple'),
            Counter('banana'),
            Counter('cherry'),
            Counter('date')
        ];

We also need to link the counters, which are JavaScript objects, to DOM nodes and events. Let’s suppose we’re to display the counters in an element whose ID is example.

This will do the job. Notice the inelegant way in which we initialise the display of the counters. Clearly this won’t do if we’re inserting say a slideshow into the page.

        var element = document.getElementById('example');

        element.innerHTML = (
            '<span id="a0">apple 0</span>'
            + '<span id="a1">banana 0</span>'
            + '<span id="a2">cherry 0</span>'
            + '<span id="a3">date 0</span>'
        );

We now need to link DOM events to the counters we created. The counters are stored in the models variable (which is local to the onload function). Each counter element on the DOM corresponds, via its ID, to a counter instance in the models array. This was done on purpose.

When a counter element is clicked we can, from its ID, find the correspond counter instance and, so to speak, click it. We can also ask the counter instance to generate new HTML for the refreshing of the counter element.

In short, everything has been set up to make delegation as easy as possible. We’ll assume that the generic function onclick_factory will handle the delegation (and cross browser issues) for us.

        element.onclick = onclick_factory(models);

This line of code helps prevent a memory leak in Internet Explorer, prior to IE8.

        element = undefined;    // Avoid IE memory leak.

And finally we can finish the onload function.

    };

Delegation

    var onclick_factory = function(models){

        var onclick = function(event){
	    
	    event = event || global.event; // For IE event handling.
	    var target = event.target || event.srcElement;
            var id = target.id;
            if (id) {
                var id_num = id.slice(1);
                var model = models[id_num];
                model.onclick();
                var html = model.html();
                if (html){
                    document.getElementById(id).innerHTML = html;
                }
            }
        };
        return onclick;

Closing namespace

    };

})();

This little piece of line noise does three things.

  1. It uses } to close the function definition.
  2. It then uses ) to close the function expresssion.
  3. Finally, it uses () to execute the function.

The final semi-colon ; closes the statement. (It’s generally best when writing code to put in the semicolons yourself, rather then let JavaScript put them in.)

Memory

Understanding garbage collection and memory leaks is an advanced topic, but even beginners should know that there can be problems here. Correct use of a suitable framework is the simplest way to avoid memory leaks.

The code in the above example indicates how to construct and use such a framework.

Garbage collection

The JavaScript garbage collection deletes objects provide it can discover that they can never be used again. The simplest case is:

x = [1, 2, 3, 4, 5, 6, 7];
x = null;

The second assignment to x ensures that the original array (created by the array literal) can no longer be accessed, and so it can be garbage collected.

After the namespace anonyomous function has executed there are no global objects holding even indirect references to the function. Therefore is can and will be garbage collected. However, as we shall see, the execution context of the function continues.

IE memory leaks

Internet Explorer, prior to IE8 (check) had two memory heaps, one for JavaScript and the other for the DOM. Each heap had its own garbage collector.

This means that if a DOM node d held a reference to a JavaScript object j and also that j held a reference to d then neither garbage could collect j or d. What’s worse, even when the page was unloaded IE did not reclaim this memory.

Thus, prior to IE8, JavaScript could cause Internet Explorer to leak memory. This lost memory could be reclaimed only by closing the browser!

Residue

The example DOM node continues to exist, as it can be reached from the document node. It has an onclick attribute, which is the function created by the onclick_factory, with models as the argument. So that function is not garbage collected.

The function example.onclick retains a reference to models just as surely as executing

var f = function(arg){ return [1, 2, 3, arg] };
var y = f(x);

causes y to retain a reference to the value of x.

Finally, models contains references to the Counter instances and thus, by the hidden prototype reference, to the counter prototype object.

The counter prototype object lies in the execution context of the anonymous function, and as it happend that keeps alive the whole of the execution context.

Reclaiming memory

Notice that the JavaScript holds only two reference to DOM nodes, namely document and example. However, the line of code

example = undefined;    // Release reference to DOM node.

means that the JavaScript does not hold a reference to example.

The DOM node example holds a reference, via its onclick function, to the JavaScript on the page, and in particular to the Counter instances. This cannot be avoided, because we want certain DOM nodes to change the state of the counters.

However, the converse is not true. We can write the JavaScript code so that it has but one reference to a DOM object, namely the document node held for example as a property of the global object.