Classes & OOP: Static Members and "Application-Global" Data

Roger PoonBy Roger Poon

JS++ Designer and Project Lead

Up until now, all of the fields and methods that we've declared have belonged to class instances. Static members can also be added to a class. A static member is initialized at program start and is available to all instances of a class.

As an example, we might want to keep a count of the number of 'Animal' instances we have. This data should not be unique to one 'Animal' instance or another; it should be the same for all animals. Let's change Animal.jspp to achieve this using the 'static' modifier:

    external $;

    module Animals
    {
        class Animal
        {
            protected var $element;
            private static unsigned int count = 0;

            protected Animal(string iconClassName) {
                string elementHTML = makeElementHTML(iconClassName);
                $element = $(elementHTML);

                Animal.count++;
            }

            public static unsigned int getCount() {
                return Animal.count;
            }

            public virtual void render() {
                $("#content").append($element);
            }

            private string makeElementHTML(string iconClassName) {
                string result = '<div class="animal">';
                result += '<i class="icofont ' + iconClassName + '"></i>';
                result += "</div>";
                return result;
            }
        }
    }
    

We've created a new field in the 'Animal' class: 'count'. It is initialized to zero at program start because it is marked 'static'. From there, on each constructor call of 'Animal' (e.g. via inheritance), we increment the 'count' static field by one using the ++ increment operator. We refer to the static field via 'Animal.count'. We actually don't need to qualify the name to this degree and can just use 'count' inside the class, but my intent was to show how 'count' should be accessed via the class name rather than an instance. Finally, we declared a getCount() method which returns the value of the static field.

Go to main.jspp and render this count to the web page using jQuery:

    import Animals;

    external $;

    Animal[] animals = [
        new Cat("Kitty"),
        new Cat("Kat"),
        new Dog("Fido"),
        new Panda(),
        new Rhino()
    ];

    foreach(Animal animal in animals) {
        animal.render();
    }

    $("#content").append("<p>Number of animals: " + Animal.getCount().toString() + "</p>");
    

Compile the project and open index.html. You should now see the count after your rendered animals:

While we've shown static field initialization at declaration, there is another way to initialize our static fields: static constructors. We can just as well change Animal.jspp to use a static constructor as follows:

    external $;

    module Animals
    {
        class Animal
        {
            protected var $element;
            private static unsigned int count;

            protected Animal(string iconClassName) {
                string elementHTML = makeElementHTML(iconClassName);
                $element = $(elementHTML);

                Animal.count++;
            }

            static Animal() {
                Animal.count = 0;
            }

            public static unsigned int getCount() {
                return Animal.count;
            }

            public virtual void render() {
                $("#content").append($element);
            }

            private string makeElementHTML(string iconClassName) {
                string result = '<div class="animal">';
                result += '<i class="icofont ' + iconClassName + '"></i>';
                result += "</div>";
                return result;
            }
        }
    }
    

Static constructors do not take any parameters, and they cannot have access modifiers applied. They will run at application start. In the code above, the 'count' static field was not initialized at declaration. Instead, we moved initialization of the 'count' field to the static constructor.

JS++ does not have a "global scope" like JavaScript does. Instead, variables that might have leaked into the global scope in JavaScript would instead be "file-scoped" in JS++ and will not be available outside the file it is declared. If you want "global variables, " you can achieve a safer subset known as "application-global data" using static members, and it's just as useful without the potential problems of global variables (e.g. unintentional leaks to other programs or libraries). Here's an example of how you can create application-global data using static members:

    module Application
    {
        class Config
        {
            public static string hostname = "127.0.0.1";
            public static unsigned int port = 80;
        }
    }
    

For more information on scoping, consult the JS++ documentation.